diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 9801ccc105..7953dc5188 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -119,6 +119,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs index cbbc327948..4278c301af 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs @@ -16,10 +16,7 @@ persistentAgentsClientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(20); // Get a client to create/retrieve server side agents with. -// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. -// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid -// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. -PersistentAgentsClient persistentAgentsClient = new(endpoint, new DefaultAzureCredential(), persistentAgentsClientOptions); +PersistentAgentsClient persistentAgentsClient = new(endpoint, new AzureCliCredential(), persistentAgentsClientOptions); // Define and configure the Deep Research tool. DeepResearchToolDefinition deepResearchTool = new(new DeepResearchDetails( diff --git a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md index dc24ba4554..1848b10826 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md @@ -23,12 +23,14 @@ Before running this sample, ensure you have: Pay special attention to the purple `Note` boxes in the Azure documentation. -**Note**: The Bing Connection ID must be from the **project**, not the resource. It has the following format: +**Note**: The Bing Grounding Connection ID must be the **full ARM resource URI** from the project, not just the connection name. It has the following format: ``` -/subscriptions//resourceGroups//providers//accounts//projects//connections/ +/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/ ``` +You can find this in the Azure AI Foundry portal under **Management > Connected resources**, or retrieve it programmatically via the connections API (`.id` property). + ## Environment Variables Set the following environment variables: @@ -37,8 +39,8 @@ Set the following environment variables: # Replace with your Azure AI Foundry project endpoint $env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/" -# Replace with your Bing connection ID from the project -$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection" +# Replace with your Bing Grounding connection ID (full ARM resource URI) +$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/" # Optional, defaults to o3-deep-research $env:AZURE_AI_REASONING_DEPLOYMENT_NAME="o3-deep-research" diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/FoundryAgentsRAPI_Step01_Basics.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/FoundryAgentsRAPI_Step01_Basics.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/FoundryAgentsRAPI_Step01_Basics.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/Program.cs new file mode 100644 index 0000000000..d59015e6b3 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and run a basic agent using the Foundry Responses API directly, +// without creating a server-side agent definition. + +using Azure.Identity; +using Microsoft.Agents.AI.AzureAI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// Create a FoundryAgentClient that uses the Responses API directly. +// No server-side agent is created — instructions and model are provided locally. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are good at telling jokes.", + name: "JokerAgent"); + +// Once you have the agent, you can invoke it like any other AIAgent. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/README.md new file mode 100644 index 0000000000..301e8c4258 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step01_Basics/README.md @@ -0,0 +1,37 @@ +# Creating and Running a Basic Agent with the Responses API + +This sample demonstrates how to create and run a basic AI agent using the `FoundryAgentClient`, which uses the Azure AI Foundry Responses API directly without creating server-side agent definitions. + +## What this sample demonstrates + +- Creating a `FoundryAgentClient` with instructions and a model +- Running a simple single-turn conversation +- No server-side agent creation or cleanup required + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +The `FoundryAgentClient` auto-discovers these environment variables — no endpoint or credential code is needed in the sample. + +## Run the sample + +Navigate to the FoundryAgents-RAPI sample directory and run: + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step01_Basics +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/FoundryAgentsRAPI_Step02_MultiturnConversation.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/FoundryAgentsRAPI_Step02_MultiturnConversation.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/FoundryAgentsRAPI_Step02_MultiturnConversation.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/Program.cs new file mode 100644 index 0000000000..18bd898c42 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/Program.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and use a multi-turn conversation agent using the Foundry Responses API directly. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// Create a FoundryAgentClient that uses the Responses API directly. +// No server-side agent is created — instructions and model are provided locally. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are good at telling jokes.", + name: "JokerAgent"); + +// Invoke the agent with a multi-turn conversation, where the context is preserved in the session object. +AgentSession session = await agent.CreateSessionAsync(); + +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); +Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)); + +// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the session object. +session = await agent.CreateSessionAsync(); +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me a joke about a pirate.", session)) +{ + Console.Write(update); +} + +Console.WriteLine(); + +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)) +{ + Console.Write(update); +} + +Console.WriteLine(); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/README.md new file mode 100644 index 0000000000..040cc0e0df --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step02_MultiturnConversation/README.md @@ -0,0 +1,39 @@ +# Multi-turn Conversation with the Responses API + +This sample demonstrates how to implement multi-turn conversations using the `FoundryAgentClient`, where context is preserved across multiple agent runs using sessions. + +## What this sample demonstrates + +- Creating a `FoundryAgentClient` with instructions +- Using sessions to maintain conversation context across multiple runs +- Running multi-turn conversations with text output +- Running multi-turn conversations with streaming output +- No server-side agent creation or cleanup required + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +The `FoundryAgentClient` auto-discovers these environment variables — no endpoint or credential code is needed in the sample. + +## Run the sample + +Navigate to the FoundryAgents-RAPI sample directory and run: + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step02_MultiturnConversation +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/FoundryAgentsRAPI_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/FoundryAgentsRAPI_Step03_UsingFunctionTools.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/FoundryAgentsRAPI_Step03_UsingFunctionTools.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/Program.cs new file mode 100644 index 0000000000..5f5f2cccc9 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/Program.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use function tools with the Foundry Responses API directly. + +using System.ComponentModel; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +[Description("Get the weather for a given location.")] +static string GetWeather([Description("The location to get the weather for.")] string location) + => $"The weather in {location} is cloudy with a high of 15°C."; + +// Define the function tool. +AITool tool = AIFunctionFactory.Create(GetWeather); + +// Create a FoundryAgentClient that uses the Responses API directly with function tools. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are a helpful assistant that can get weather information.", + name: "WeatherAssistant", + tools: [tool]); + +// Non-streaming agent interaction with function tools. +AgentSession session = await agent.CreateSessionAsync(); +Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session)); + +// Streaming agent interaction with function tools. +session = await agent.CreateSessionAsync(); +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", session)) +{ + Console.Write(update); +} + +Console.WriteLine(); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/README.md new file mode 100644 index 0000000000..3992d416de --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step03_UsingFunctionTools/README.md @@ -0,0 +1,39 @@ +# Using Function Tools with the Responses API + +This sample demonstrates how to use function tools with the `FoundryAgentClient`, allowing the agent to call custom functions to retrieve information. + +## What this sample demonstrates + +- Creating function tools using `AIFunctionFactory` +- Passing function tools to a `FoundryAgentClient` +- Running agents with function tools (text output) +- Running agents with function tools (streaming output) +- No server-side agent creation or cleanup required + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +The `FoundryAgentClient` auto-discovers these environment variables — no endpoint or credential code is needed in the sample. + +## Run the sample + +Navigate to the FoundryAgents-RAPI sample directory and run: + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step03_UsingFunctionTools +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/Program.cs new file mode 100644 index 0000000000..b7c4539707 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/Program.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use an agent with function tools that require a human in the loop for approvals. + +using System.ComponentModel; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +[Description("Get the weather for a given location.")] +static string GetWeather([Description("The location to get the weather for.")] string location) + => $"The weather in {location} is cloudy with a high of 15°C."; + +ApprovalRequiredAIFunction approvalTool = new(AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))); + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are a helpful assistant that can get weather information.", + name: "WeatherAssistant", + tools: [approvalTool]); + +// Call the agent with approval-required function tools. +AgentSession session = await agent.CreateSessionAsync(); +AgentResponse response = await agent.RunAsync("What is the weather like in Amsterdam?", session); + +// Check if there are any approval requests. +List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + +while (approvalRequests.Count > 0) +{ + // Ask the user to approve each function call request. + List userInputMessages = approvalRequests + .ConvertAll(functionApprovalRequest => + { + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + bool approved = Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false; + return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved)]); + }); + + response = await agent.RunAsync(userInputMessages, session); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); +} + +Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/README.md new file mode 100644 index 0000000000..cd816f55a6 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/README.md @@ -0,0 +1,30 @@ +# Using Function Tools with Approvals via the Responses API + +This sample demonstrates how to use function tools that require human-in-the-loop approval before execution. + +## What this sample demonstrates + +- Creating function tools that require approval using `ApprovalRequiredAIFunction` +- Handling approval requests from the agent +- Passing approval responses back to the agent +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/FoundryAgentsRAPI_Step05_StructuredOutput.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/FoundryAgentsRAPI_Step05_StructuredOutput.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/FoundryAgentsRAPI_Step05_StructuredOutput.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/Program.cs new file mode 100644 index 0000000000..0bc79dbc20 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/Program.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to configure an agent to produce structured output using the Responses API directly. + +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using SampleApp; + +#pragma warning disable CA5399 + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + options: new ChatClientAgentOptions + { + Name = "StructuredOutputAssistant", + ChatOptions = new() + { + ModelId = deploymentName, + Instructions = "You are a helpful assistant that extracts structured information about people.", + ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema() + } + }); + +// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output. +AgentResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer."); + +// Access the structured output via the Result property of the agent response. +Console.WriteLine("Assistant Output:"); +Console.WriteLine($"Name: {response.Result.Name}"); +Console.WriteLine($"Age: {response.Result.Age}"); +Console.WriteLine($"Occupation: {response.Result.Occupation}"); + +// Invoke the agent with streaming support, then deserialize the assembled response. +IAsyncEnumerable updates = agent.RunStreamingAsync("Please provide information about Jane Doe, who is a 28-year-old data scientist."); + +PersonInfo personInfo = JsonSerializer.Deserialize((await updates.ToAgentResponseAsync()).Text, JsonSerializerOptions.Web) + ?? throw new InvalidOperationException("Failed to deserialize the streamed response into PersonInfo."); + +Console.WriteLine("\nStreaming Assistant Output:"); +Console.WriteLine($"Name: {personInfo.Name}"); +Console.WriteLine($"Age: {personInfo.Age}"); +Console.WriteLine($"Occupation: {personInfo.Occupation}"); + +namespace SampleApp +{ + /// + /// Represents information about a person. + /// + [Description("Information about a person including their name, age, and occupation")] + public class PersonInfo + { + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("age")] + public int? Age { get; set; } + + [JsonPropertyName("occupation")] + public string? Occupation { get; set; } + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/README.md new file mode 100644 index 0000000000..05ea8e7481 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step05_StructuredOutput/README.md @@ -0,0 +1,29 @@ +# Structured Output with the Responses API + +This sample demonstrates how to configure an agent to produce structured output using JSON schema. + +## What this sample demonstrates + +- Using `RunAsync()` to get typed structured output from the agent +- Deserializing streamed responses into structured types +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step05_StructuredOutput +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/FoundryAgentsRAPI_Step06_PersistedConversations.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/FoundryAgentsRAPI_Step06_PersistedConversations.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/FoundryAgentsRAPI_Step06_PersistedConversations.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/Program.cs new file mode 100644 index 0000000000..156ced748c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/Program.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to persist and resume conversations using the Responses API directly. + +using System.Text.Json; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are good at telling jokes.", + name: "JokerAgent"); + +// Start a new session for the agent conversation. +AgentSession session = await agent.CreateSessionAsync(); + +// Run the agent with a new session. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); + +// Serialize the session state to a JsonElement, so it can be stored for later use. +JsonElement serializedSession = await agent.SerializeSessionAsync(session); + +// Save the serialized session to a temporary file (for demonstration purposes). +string tempFilePath = Path.GetTempFileName(); +await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedSession)); + +// Load the serialized session from the temporary file (for demonstration purposes). +JsonElement reloadedSerializedSession = JsonElement.Parse(await File.ReadAllTextAsync(tempFilePath))!; + +// Deserialize the session state after loading from storage. +AgentSession resumedSession = await agent.DeserializeSessionAsync(reloadedSerializedSession); + +// Run the agent again with the resumed session. +Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedSession)); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/README.md new file mode 100644 index 0000000000..2725616fd1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step06_PersistedConversations/README.md @@ -0,0 +1,30 @@ +# Persisted Conversations with the Responses API + +This sample demonstrates how to persist and resume agent conversations using session serialization. + +## What this sample demonstrates + +- Serializing agent sessions to JSON for persistence +- Saving and loading sessions from disk +- Resuming conversations with preserved context +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step06_PersistedConversations +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/FoundryAgentsRAPI_Step07_Observability.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/FoundryAgentsRAPI_Step07_Observability.csproj new file mode 100644 index 0000000000..5350f8236d --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/FoundryAgentsRAPI_Step07_Observability.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/Program.cs new file mode 100644 index 0000000000..38ab9d9180 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/Program.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to add OpenTelemetry observability to an agent using the Responses API directly. + +using Azure.Identity; +using Azure.Monitor.OpenTelemetry.Exporter; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using OpenTelemetry; +using OpenTelemetry.Trace; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string? applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING"); + +// Create TracerProvider with console exporter. +string sourceName = Guid.NewGuid().ToString("N"); +TracerProviderBuilder tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() + .AddSource(sourceName) + .AddConsoleExporter(); +if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString)) +{ + tracerProviderBuilder.AddAzureMonitorTraceExporter(options => options.ConnectionString = applicationInsightsConnectionString); +} +using var tracerProvider = tracerProviderBuilder.Build(); + +// Create a FoundryAgentClient using environment variable auto-discovery. +// AZURE_AI_PROJECT_ENDPOINT - The Azure AI Foundry project endpoint URL. +// AZURE_AI_MODEL_DEPLOYMENT_NAME - The model deployment name to use. +AIAgent agent = new FoundryAgentClient( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are good at telling jokes.", + name: "JokerAgent") + .AsBuilder() + .UseOpenTelemetry(sourceName: sourceName) + .Build(); + +// Invoke the agent and output the text result. +AgentSession session = await agent.CreateSessionAsync(); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); + +// Invoke the agent with streaming support. +session = await agent.CreateSessionAsync(); +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me a joke about a pirate.", session)) +{ + Console.Write(update); +} + +Console.WriteLine(); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/README.md new file mode 100644 index 0000000000..c173477791 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step07_Observability/README.md @@ -0,0 +1,31 @@ +# Observability with the Responses API + +This sample demonstrates how to add OpenTelemetry observability to an agent using console and Azure Monitor exporters. + +## What this sample demonstrates + +- Configuring OpenTelemetry tracing with console exporter +- Optional Azure Application Insights integration +- Using `.AsBuilder().UseOpenTelemetry()` to add telemetry to the agent +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +$env:APPLICATIONINSIGHTS_CONNECTION_STRING="..." # Optional +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step07_Observability +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/FoundryAgentsRAPI_Step08_DependencyInjection.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/FoundryAgentsRAPI_Step08_DependencyInjection.csproj new file mode 100644 index 0000000000..ffa39538d5 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/FoundryAgentsRAPI_Step08_DependencyInjection.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + + $(NoWarn);CA1812 + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/Program.cs new file mode 100644 index 0000000000..5f403ea02f --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/Program.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use dependency injection to register a FoundryAgentClient and use it from a hosted service. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are good at telling jokes.", + name: "JokerAgent"); + +// Create a host builder that we will register services with and then run. +HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); + +// Add the AI agent to the service collection. +builder.Services.AddSingleton(agent); + +// Add a sample service that will use the agent to respond to user input. +builder.Services.AddHostedService(); + +// Build and run the host. +using IHost host = builder.Build(); +await host.RunAsync().ConfigureAwait(false); + +/// +/// A sample service that uses an AI agent to respond to user input. +/// +internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService +{ + private AgentSession? _session; + + public async Task StartAsync(CancellationToken cancellationToken) + { + this._session = await agent.CreateSessionAsync(cancellationToken); + _ = this.RunAsync(appLifetime.ApplicationStopping); + } + + public async Task RunAsync(CancellationToken cancellationToken) + { + await Task.Delay(100, cancellationToken); + + while (!cancellationToken.IsCancellationRequested) + { + Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n"); + Console.Write("> "); + string? input = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(input)) + { + appLifetime.StopApplication(); + break; + } + + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken)) + { + Console.Write(update); + } + + Console.WriteLine(); + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("\nShutting down..."); + return Task.CompletedTask; + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/README.md new file mode 100644 index 0000000000..d27e091f28 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step08_DependencyInjection/README.md @@ -0,0 +1,30 @@ +# Dependency Injection with the Responses API + +This sample demonstrates how to register a `FoundryAgentClient` in a dependency injection container and use it from a hosted service. + +## What this sample demonstrates + +- Registering `FoundryAgentClient` as an `AIAgent` in the service collection +- Using the agent from a `IHostedService` with an interactive chat loop +- Streaming responses in a hosted service context +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step08_DependencyInjection +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools.csproj new file mode 100644 index 0000000000..a69c81f08c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools.csproj @@ -0,0 +1,20 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/Program.cs new file mode 100644 index 0000000000..cb090912c1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/Program.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use MCP client tools with a FoundryAgentClient using the Responses API directly. + +using Azure.Identity; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using ModelContextProtocol.Client; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +Console.WriteLine("Starting MCP Stdio for @modelcontextprotocol/server-github ... "); + +// Create an MCPClient for the GitHub server +await using var mcpClient = await McpClient.CreateAsync(new StdioClientTransport(new() +{ + Name = "MCPServer", + Command = "npx", + Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-github"], +})); + +// Retrieve the list of tools available on the GitHub server +IList mcpTools = await mcpClient.ListToolsAsync(); + +// Create a FoundryAgentClient that uses the Responses API directly with MCP tools. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You answer questions related to GitHub repositories only.", + name: "AgentWithMCP", + tools: [.. mcpTools.Cast()]); + +string prompt = "Summarize the last four commits to the microsoft/semantic-kernel repository?"; + +Console.WriteLine($"Invoking agent '{agent.Name}' with prompt: {prompt} ..."); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync(prompt)); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/README.md new file mode 100644 index 0000000000..741ce4519d --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/README.md @@ -0,0 +1,29 @@ +# Using MCP Client as Tools with the Responses API + +This sample shows how to use MCP (Model Context Protocol) client tools with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Connecting to an MCP server (GitHub) via stdio transport +- Retrieving MCP tools and passing them to a `FoundryAgentClient` +- Using MCP tools for agent interactions without server-side agent creation + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) +- Node.js installed (for npx/MCP server) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/FoundryAgentsRAPI_Step10_UsingImages.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/FoundryAgentsRAPI_Step10_UsingImages.csproj new file mode 100644 index 0000000000..91ad19c6ab --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/FoundryAgentsRAPI_Step10_UsingImages.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + PreserveNewest + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/Program.cs new file mode 100644 index 0000000000..4500eb1a97 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/Program.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use image multi-modality with an agent using the Responses API directly. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o"; + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are a helpful agent that can analyze images.", + name: "VisionAgent"); + +ChatMessage message = new(ChatRole.User, [ + new TextContent("What do you see in this image?"), + await DataContent.LoadFromAsync("assets/walkway.jpg"), +]); + +AgentSession session = await agent.CreateSessionAsync(); + +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(message, session)) +{ + Console.Write(update); +} + +Console.WriteLine(); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/README.md new file mode 100644 index 0000000000..2f80ac87df --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/README.md @@ -0,0 +1,30 @@ +# Using Images with the Responses API + +This sample demonstrates how to use image multi-modality with an agent. + +## What this sample demonstrates + +- Loading images using `DataContent.LoadFromAsync` +- Sending images alongside text to the agent +- Streaming the agent's image analysis response +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and a vision-capable model deployment (e.g., `gpt-4o`) +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step10_UsingImages +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/assets/walkway.jpg b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/assets/walkway.jpg new file mode 100644 index 0000000000..13ef1e1840 Binary files /dev/null and b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step10_UsingImages/assets/walkway.jpg differ diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/FoundryAgentsRAPI_Step11_AsFunctionTool.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/FoundryAgentsRAPI_Step11_AsFunctionTool.csproj new file mode 100644 index 0000000000..5e73fd236a --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/FoundryAgentsRAPI_Step11_AsFunctionTool.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/Program.cs new file mode 100644 index 0000000000..989a90da56 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/Program.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use one agent as a function tool for another agent using the Responses API directly. + +using System.ComponentModel; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +[Description("Get the weather for a given location.")] +static string GetWeather([Description("The location to get the weather for.")] string location) + => $"The weather in {location} is cloudy with a high of 15°C."; + +AITool weatherTool = AIFunctionFactory.Create(GetWeather); +FoundryAgentClient weatherAgent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You answer questions about the weather.", + name: "WeatherAgent", + tools: [weatherTool]); + +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are a helpful assistant who responds in French.", + name: "MainAgent", + tools: [weatherAgent.AsAIFunction()]); + +// Invoke the agent and output the text result. +AgentSession session = await agent.CreateSessionAsync(); +Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session)); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/README.md new file mode 100644 index 0000000000..ee18e9e6d4 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step11_AsFunctionTool/README.md @@ -0,0 +1,30 @@ +# Agent as a Function Tool with the Responses API + +This sample demonstrates how to use one agent as a function tool for another agent. + +## What this sample demonstrates + +- Creating a specialized agent (weather) with function tools +- Exposing an agent as a function tool using `.AsAIFunction()` +- Composing agents where one agent delegates to another +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step11_AsFunctionTool +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/FoundryAgentsRAPI_Step12_Middleware.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/FoundryAgentsRAPI_Step12_Middleware.csproj new file mode 100644 index 0000000000..811a1eacb6 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/FoundryAgentsRAPI_Step12_Middleware.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/Program.cs new file mode 100644 index 0000000000..ed51d47b09 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/Program.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows multiple middleware layers working together with the Responses API: +// agent run (PII filtering and guardrails), +// function invocation (logging and result overrides), and human-in-the-loop +// approval workflows for sensitive function calls. + +using System.ComponentModel; +using System.Text.RegularExpressions; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +[Description("Get the weather for a given location.")] +static string GetWeather([Description("The location to get the weather for.")] string location) + => $"The weather in {location} is cloudy with a high of 15°C."; + +[Description("The current datetime offset.")] +static string GetDateTime() + => DateTimeOffset.Now.ToString(); + +AITool dateTimeTool = AIFunctionFactory.Create(GetDateTime, name: nameof(GetDateTime)); +AITool getWeatherTool = AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather)); + +FoundryAgentClient originalAgent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are an AI assistant that helps people find information.", + name: "InformationAssistant", + tools: [getWeatherTool, dateTimeTool]); + +// Adding middleware to the agent level +AIAgent middlewareEnabledAgent = originalAgent + .AsBuilder() + .Use(FunctionCallMiddleware) + .Use(FunctionCallOverrideWeather) + .Use(PIIMiddleware, null) + .Use(GuardrailMiddleware, null) + .Build(); + +AgentSession session = await middlewareEnabledAgent.CreateSessionAsync(); + +Console.WriteLine("\n\n=== Example 1: Wording Guardrail ==="); +AgentResponse guardRailedResponse = await middlewareEnabledAgent.RunAsync("Tell me something harmful."); +Console.WriteLine($"Guard railed response: {guardRailedResponse}"); + +Console.WriteLine("\n\n=== Example 2: PII detection ==="); +AgentResponse piiResponse = await middlewareEnabledAgent.RunAsync("My name is John Doe, call me at 123-456-7890 or email me at john@something.com"); +Console.WriteLine($"Pii filtered response: {piiResponse}"); + +Console.WriteLine("\n\n=== Example 3: Agent function middleware ==="); +AgentResponse functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", session); +Console.WriteLine($"Function calling response: {functionCallResponse}"); + +// Special per-request middleware agent. +Console.WriteLine("\n\n=== Example 4: Middleware with human in the loop function approval ==="); + +FoundryAgentClient humanInTheLoopAgent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: "You are a Human in the loop testing AI assistant that helps people find information.", + name: "HumanInTheLoopAgent", + tools: [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather)))]); + +AgentResponse response = await humanInTheLoopAgent + .AsBuilder() + .Use(ConsolePromptingApprovalMiddleware, null) + .Build() + .RunAsync("What's the current time and the weather in Seattle?"); + +Console.WriteLine($"HumanInTheLoopAgent agent middleware response: {response}"); + +// Function invocation middleware that logs before and after function calls. +async ValueTask FunctionCallMiddleware(AIAgent agent, FunctionInvocationContext context, Func> next, CancellationToken cancellationToken) +{ + Console.WriteLine($"Function Name: {context!.Function.Name} - Middleware 1 Pre-Invoke"); + var result = await next(context, cancellationToken); + Console.WriteLine($"Function Name: {context!.Function.Name} - Middleware 1 Post-Invoke"); + + return result; +} + +// Function invocation middleware that overrides the result of the GetWeather function. +async ValueTask FunctionCallOverrideWeather(AIAgent agent, FunctionInvocationContext context, Func> next, CancellationToken cancellationToken) +{ + Console.WriteLine($"Function Name: {context!.Function.Name} - Middleware 2 Pre-Invoke"); + + var result = await next(context, cancellationToken); + + if (context.Function.Name == nameof(GetWeather)) + { + result = "The weather is sunny with a high of 25°C."; + } + Console.WriteLine($"Function Name: {context!.Function.Name} - Middleware 2 Post-Invoke"); + return result; +} + +// This middleware redacts PII information from input and output messages. +async Task PIIMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +{ + var filteredMessages = FilterMessages(messages); + Console.WriteLine("Pii Middleware - Filtered Messages Pre-Run"); + + var agentResponse = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken).ConfigureAwait(false); + + agentResponse.Messages = FilterMessages(agentResponse.Messages); + + Console.WriteLine("Pii Middleware - Filtered Messages Post-Run"); + + return agentResponse; + + static IList FilterMessages(IEnumerable messages) + { + return messages.Select(m => new ChatMessage(m.Role, FilterPii(m.Text))).ToList(); + } + + static string FilterPii(string content) + { + Regex[] piiPatterns = [ + new(@"\b\d{3}-\d{3}-\d{4}\b", RegexOptions.Compiled), + new(@"\b[\w\.-]+@[\w\.-]+\.\w+\b", RegexOptions.Compiled), + new(@"\b[A-Z][a-z]+\s[A-Z][a-z]+\b", RegexOptions.Compiled) + ]; + + foreach (var pattern in piiPatterns) + { + content = pattern.Replace(content, "[REDACTED: PII]"); + } + + return content; + } +} + +// This middleware enforces guardrails by redacting certain keywords from input and output messages. +async Task GuardrailMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +{ + var filteredMessages = FilterMessages(messages); + + Console.WriteLine("Guardrail Middleware - Filtered messages Pre-Run"); + + var agentResponse = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken); + + agentResponse.Messages = FilterMessages(agentResponse.Messages); + + Console.WriteLine("Guardrail Middleware - Filtered messages Post-Run"); + + return agentResponse; + + List FilterMessages(IEnumerable messages) + { + return messages.Select(m => new ChatMessage(m.Role, FilterContent(m.Text))).ToList(); + } + + static string FilterContent(string content) + { + foreach (var keyword in new[] { "harmful", "illegal", "violence" }) + { + if (content.Contains(keyword, StringComparison.OrdinalIgnoreCase)) + { + return "[REDACTED: Forbidden content]"; + } + } + + return content; + } +} + +// This middleware handles Human in the loop console interaction for any user approval required during function calling. +async Task ConsolePromptingApprovalMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +{ + AgentResponse agentResponse = await innerAgent.RunAsync(messages, session, options, cancellationToken); + + List approvalRequests = agentResponse.Messages.SelectMany(m => m.Contents).OfType().ToList(); + + while (approvalRequests.Count > 0) + { + agentResponse.Messages = approvalRequests + .ConvertAll(functionApprovalRequest => + { + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + bool approved = Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false; + return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved)]); + }); + + agentResponse = await innerAgent.RunAsync(agentResponse.Messages, session, options, cancellationToken); + + approvalRequests = agentResponse.Messages.SelectMany(m => m.Contents).OfType().ToList(); + } + + return agentResponse; +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/README.md new file mode 100644 index 0000000000..0c0be12cbd --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step12_Middleware/README.md @@ -0,0 +1,31 @@ +# Middleware with the Responses API + +This sample demonstrates multiple middleware layers working together: PII filtering, guardrails, function invocation logging, and human-in-the-loop approval. + +## What this sample demonstrates + +- Agent-level run middleware (PII filtering, guardrail enforcement) +- Function-level middleware (logging, result overrides) +- Human-in-the-loop approval workflows for sensitive function calls +- Using `.AsBuilder().Use()` to compose middleware +- No server-side agent creation or cleanup required + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +cd dotnet/samples/02-agents/FoundryAgents-RAPI +dotnet run --project .\FoundryAgentsRAPI_Step12_Middleware +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/FoundryAgentsRAPI_Step13_Plugins.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/FoundryAgentsRAPI_Step13_Plugins.csproj new file mode 100644 index 0000000000..489a070620 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/FoundryAgentsRAPI_Step13_Plugins.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + $(NoWarn);CA1812 + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/Program.cs new file mode 100644 index 0000000000..e9b1630d95 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/Program.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use plugins with a FoundryAgentClient using the Responses API directly. +// Plugin classes can depend on other services that need to be injected. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.DependencyInjection; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AssistantInstructions = "You are a helpful assistant that helps people find information."; +const string AssistantName = "PluginAssistant"; + +// Create a service collection to hold the agent plugin and its dependencies. +ServiceCollection services = new(); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); // The plugin depends on WeatherProvider and CurrentTimeProvider registered above. + +IServiceProvider serviceProvider = services.BuildServiceProvider(); + +// Create a FoundryAgentClient with the options-based constructor to pass services. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + clientOptions: null, + options: new ChatClientAgentOptions + { + Name = AssistantName, + ChatOptions = new() { ModelId = deploymentName, Instructions = AssistantInstructions, Tools = serviceProvider.GetRequiredService().AsAITools().ToList() } + }, + services: serviceProvider); + +// Invoke the agent and output the text result. +AgentSession session = await agent.CreateSessionAsync(); +Console.WriteLine(await agent.RunAsync("Tell me current time and weather in Seattle.", session)); + +/// +/// The agent plugin that provides weather and current time information. +/// +/// The weather provider to get weather information. +internal sealed class AgentPlugin(WeatherProvider weatherProvider) +{ + /// + /// Gets the weather information for the specified location. + /// + /// The location to get the weather for. + /// The weather information for the specified location. + public string GetWeather(string location) + { + return weatherProvider.GetWeather(location); + } + + /// + /// Gets the current date and time for the specified location. + /// + /// The service provider to resolve the . + /// The location to get the current time for. + /// The current date and time as a . + public DateTimeOffset GetCurrentTime(IServiceProvider sp, string location) + { + CurrentTimeProvider currentTimeProvider = sp.GetRequiredService(); + return currentTimeProvider.GetCurrentTime(location); + } + + /// + /// Returns the functions provided by this plugin. + /// + /// The functions provided by this plugin. + public IEnumerable AsAITools() + { + yield return AIFunctionFactory.Create(this.GetWeather); + yield return AIFunctionFactory.Create(this.GetCurrentTime); + } +} + +/// +/// The weather provider that returns weather information. +/// +internal sealed class WeatherProvider +{ + /// + /// Gets the weather information for the specified location. + /// + /// The location to get the weather for. + /// The weather information for the specified location. + public string GetWeather(string location) + { + return $"The weather in {location} is cloudy with a high of 15°C."; + } +} + +/// +/// Provides the current date and time. +/// +internal sealed class CurrentTimeProvider +{ + /// + /// Gets the current date and time. + /// + /// The location to get the current time for (not used in this implementation). + /// The current date and time as a . + public DateTimeOffset GetCurrentTime(string location) + { + return DateTimeOffset.Now; + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/README.md new file mode 100644 index 0000000000..3b36f30e09 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step13_Plugins/README.md @@ -0,0 +1,29 @@ +# Using Plugins with the Responses API + +This sample shows how to use plugins with a `FoundryAgentClient` using the Responses API directly, with dependency injection for plugin services. + +## What this sample demonstrates + +- Creating plugin classes with injected dependencies +- Registering services and building a service provider +- Passing `services` to the `FoundryAgentClient` via the options-based constructor +- Using `AIFunctionFactory` to expose plugin methods as AI tools + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/FoundryAgentsRAPI_Step14_CodeInterpreter.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/FoundryAgentsRAPI_Step14_CodeInterpreter.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/FoundryAgentsRAPI_Step14_CodeInterpreter.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/Program.cs new file mode 100644 index 0000000000..295413b0ed --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/Program.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Code Interpreter Tool with a FoundryAgentClient using the Responses API directly. + +using System.Text; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using OpenAI.Assistants; +using OpenAI.Responses; + +string endpoint= Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a personal math tutor. When asked a math question, write and run code using the python tool to answer the question."; +const string AgentName = "CoderAgent-RAPI"; + +// Create a FoundryAgentClient with HostedCodeInterpreterTool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: AgentName, + tools: [new HostedCodeInterpreterTool() { Inputs = [] }]); + +AgentResponse response = await agent.RunAsync("I need to solve the equation sin(x) + x^2 = 42"); + +// Get the CodeInterpreterToolCallContent +CodeInterpreterToolCallContent? toolCallContent = response.Messages.SelectMany(m => m.Contents).OfType().FirstOrDefault(); +if (toolCallContent?.Inputs is not null) +{ + DataContent? codeInput = toolCallContent.Inputs.OfType().FirstOrDefault(); + if (codeInput?.HasTopLevelMediaType("text") ?? false) + { + Console.WriteLine($"Code Input: {Encoding.UTF8.GetString(codeInput.Data.ToArray()) ?? "Not available"}"); + } +} + +// Get the CodeInterpreterToolResultContent +CodeInterpreterToolResultContent? toolResultContent = response.Messages.SelectMany(m => m.Contents).OfType().FirstOrDefault(); +if (toolResultContent?.Outputs is not null && toolResultContent.Outputs.OfType().FirstOrDefault() is { } resultOutput) +{ + Console.WriteLine($"Code Tool Result: {resultOutput.Text}"); +} + +// Getting any annotations generated by the tool +foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(C => C.Annotations ?? [])) +{ + if (annotation.RawRepresentation is TextAnnotationUpdate citationAnnotation) + { + Console.WriteLine($$""" + File Id: {{citationAnnotation.OutputFileId}} + Text to Replace: {{citationAnnotation.TextToReplace}} + Filename: {{Path.GetFileName(citationAnnotation.TextToReplace)}} + """); + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/README.md new file mode 100644 index 0000000000..5bea8dd540 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step14_CodeInterpreter/README.md @@ -0,0 +1,28 @@ +# Code Interpreter with the Responses API + +This sample shows how to use the Code Interpreter tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Using `HostedCodeInterpreterTool` with `FoundryAgentClient` +- Extracting code input and output from agent responses +- Handling code interpreter annotations and file citations + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_browser_search.png b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_browser_search.png new file mode 100644 index 0000000000..5984b95cb3 Binary files /dev/null and b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_browser_search.png differ diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_results.png b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_results.png new file mode 100644 index 0000000000..ed3ab3d8d4 Binary files /dev/null and b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_results.png differ diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_typed.png b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_typed.png new file mode 100644 index 0000000000..04d76e2075 Binary files /dev/null and b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Assets/cua_search_typed.png differ diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/ComputerUseUtil.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/ComputerUseUtil.cs new file mode 100644 index 0000000000..1ee421b465 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/ComputerUseUtil.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. + +using OpenAI.Responses; + +namespace Demo.ComputerUse; + +/// +/// Enum for tracking the state of the simulated web search flow. +/// +internal enum SearchState +{ + Initial, // Browser search page + Typed, // Text entered in search box + PressedEnter // Enter key pressed, transitioning to results +} + +internal static class ComputerUseUtil +{ + /// + /// Load and convert screenshot images to base64 data URLs. + /// + internal static Dictionary LoadScreenshotAssets() + { + string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets"); + + ReadOnlySpan<(string key, string fileName)> screenshotFiles = + [ + ("browser_search", "cua_browser_search.png"), + ("search_typed", "cua_search_typed.png"), + ("search_results", "cua_search_results.png") + ]; + + Dictionary screenshots = []; + foreach (var (key, fileName) in screenshotFiles) + { + string fullPath = Path.GetFullPath(Path.Combine(baseDir, fileName)); + screenshots[key] = File.ReadAllBytes(fullPath); + } + + return screenshots; + } + + /// + /// Process a computer action and simulate its execution. + /// + internal static (SearchState CurrentState, byte[] ImageBytes) HandleComputerActionAndTakeScreenshot( + ComputerCallAction action, + SearchState currentState, + Dictionary screenshots) + { + Console.WriteLine($"Simulating the execution of computer action: {action.Kind}"); + + SearchState newState = DetermineNextState(action, currentState); + string imageKey = GetImageKey(newState); + + return (newState, screenshots[imageKey]); + } + + private static SearchState DetermineNextState(ComputerCallAction action, SearchState currentState) + { + string actionType = action.Kind.ToString(); + + if (actionType.Equals("type", StringComparison.OrdinalIgnoreCase) && action.TypeText is not null) + { + return SearchState.Typed; + } + + if (IsEnterKeyAction(action, actionType)) + { + Console.WriteLine(" -> Detected ENTER key press"); + return SearchState.PressedEnter; + } + + if (actionType.Equals("click", StringComparison.OrdinalIgnoreCase) && currentState == SearchState.Typed) + { + Console.WriteLine(" -> Detected click after typing"); + return SearchState.PressedEnter; + } + + return currentState; + } + + private static bool IsEnterKeyAction(ComputerCallAction action, string actionType) + { + return (actionType.Equals("key", StringComparison.OrdinalIgnoreCase) || + actionType.Equals("keypress", StringComparison.OrdinalIgnoreCase)) && + action.KeyPressKeyCodes is not null && + (action.KeyPressKeyCodes.Contains("Return", StringComparer.OrdinalIgnoreCase) || + action.KeyPressKeyCodes.Contains("Enter", StringComparer.OrdinalIgnoreCase)); + } + + private static string GetImageKey(SearchState state) => state switch + { + SearchState.PressedEnter => "search_results", + SearchState.Typed => "search_typed", + _ => "browser_search" + }; +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/FoundryAgentsRAPI_Step15_ComputerUse.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/FoundryAgentsRAPI_Step15_ComputerUse.csproj new file mode 100644 index 0000000000..e70524fce1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/FoundryAgentsRAPI_Step15_ComputerUse.csproj @@ -0,0 +1,33 @@ + + + + Exe + net10.0 + + enable + enable + $(NoWarn);OPENAICUA001 + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Program.cs new file mode 100644 index 0000000000..9d5c68a270 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/Program.cs @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Computer Use Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using OpenAI.Responses; + +namespace Demo.ComputerUse; + +internal sealed class Program +{ + private static async Task Main(string[] args) + { + string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); + string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "computer-use-preview"; + + const string AgentInstructions = @" + You are a computer automation assistant. + + Be direct and efficient. When you reach the search results page, read and describe the actual search result titles and descriptions you can see. + "; + + const string AgentName = "ComputerAgent-RAPI"; + + // Create a FoundryAgentClient with ComputerUseTool using the Responses API directly. + // No server-side agent is created. + FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: AgentName, + description: "Computer automation agent with screen interaction capabilities.", + tools: [ + ResponseTool.CreateComputerTool(ComputerToolEnvironment.Browser, 1026, 769).AsAITool(), + ]); + + await InvokeComputerUseAgentAsync(agent); + } + + private static async Task InvokeComputerUseAgentAsync(AIAgent agent) + { + // Load screenshot assets + Dictionary screenshots = ComputerUseUtil.LoadScreenshotAssets(); + + ChatOptions chatOptions = new(); + CreateResponseOptions responseCreationOptions = new() + { + TruncationMode = ResponseTruncationMode.Auto + }; + chatOptions.RawRepresentationFactory = (_) => responseCreationOptions; + ChatClientAgentRunOptions runOptions = new(chatOptions) + { + AllowBackgroundResponses = true, + }; + + ChatMessage message = new(ChatRole.User, [ + new TextContent("I need you to help me search for 'OpenAI news'. Please type 'OpenAI news' and submit the search. Once you see search results, the task is complete."), + new DataContent(new BinaryData(screenshots["browser_search"]), "image/png") + ]); + + // Initial request with screenshot - start with Bing search page + Console.WriteLine("Starting computer automation session (initial screenshot: cua_browser_search.png)..."); + + // With RAPI (no server-side agent), we use PreviousResponseId to chain calls, + // sending only the new computer_call_output items instead of re-sending the full context. + AgentSession session = await agent.CreateSessionAsync(); + AgentResponse response = await agent.RunAsync(message, session: session, options: runOptions); + + // Main interaction loop + const int MaxIterations = 10; + int iteration = 0; + // Initialize state machine + SearchState currentState = SearchState.Initial; + + while (true) + { + // Poll until the response is complete. + while (response.ContinuationToken is { } token) + { + // Wait before polling again. + await Task.Delay(TimeSpan.FromSeconds(2)); + + // Continue with the token. + runOptions.ContinuationToken = token; + + response = await agent.RunAsync(session, runOptions); + } + + // Clear the continuation token so the next RunAsync call is a fresh request. + runOptions.ContinuationToken = null; + + Console.WriteLine($"Agent response received (ID: {response.ResponseId})"); + + if (iteration >= MaxIterations) + { + Console.WriteLine($"\nReached maximum iterations ({MaxIterations}). Stopping."); + break; + } + + iteration++; + Console.WriteLine($"\n--- Iteration {iteration} ---"); + + // Check for computer calls in the response + IEnumerable computerCallResponseItems = response.Messages + .SelectMany(x => x.Contents) + .Where(c => c.RawRepresentation is ComputerCallResponseItem and not null) + .Select(c => (ComputerCallResponseItem)c.RawRepresentation!); + + ComputerCallResponseItem? firstComputerCall = computerCallResponseItems.FirstOrDefault(); + if (firstComputerCall is null) + { + Console.WriteLine("No computer call actions found. Ending interaction."); + Console.WriteLine($"Final Response: {response}"); + break; + } + + // Process the first computer call response + ComputerCallAction action = firstComputerCall.Action; + string currentCallId = firstComputerCall.CallId; + + Console.WriteLine($"Processing computer call (ID: {currentCallId})"); + + // Simulate executing the action and taking a screenshot + (SearchState CurrentState, byte[] ImageBytes) screenInfo = ComputerUseUtil.HandleComputerActionAndTakeScreenshot(action, currentState, screenshots); + currentState = screenInfo.CurrentState; + + Console.WriteLine("Sending action result back to agent..."); + + // Send only the computer_call_output — the session carries PreviousResponseId for context continuity. + AIContent callOutput = new() + { + RawRepresentation = new ComputerCallOutputResponseItem( + currentCallId, + output: ComputerCallOutput.CreateScreenshotOutput(new BinaryData(screenInfo.ImageBytes), "image/png")) + }; + + response = await agent.RunAsync([new ChatMessage(ChatRole.User, [callOutput])], session: session, options: runOptions); + } + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/README.md new file mode 100644 index 0000000000..0ad3bd784b --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step15_ComputerUse/README.md @@ -0,0 +1,29 @@ +# Computer Use with the Responses API + +This sample shows how to use the Computer Use tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Using `ResponseTool.CreateComputerTool()` with `FoundryAgentClient` +- Processing computer call actions (click, type, key press) +- Managing the computer use interaction loop with screenshots +- Handling the Azure Agents API workaround for `previous_response_id` with `computer_call_output` + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="computer-use-preview" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/FoundryAgentsRAPI_Step16_FileSearch.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/FoundryAgentsRAPI_Step16_FileSearch.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/FoundryAgentsRAPI_Step16_FileSearch.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/Program.cs new file mode 100644 index 0000000000..fb6b461623 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/Program.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use File Search Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.AI.Projects; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using OpenAI.Assistants; +using OpenAI.Files; +using OpenAI.Responses; + +string endpoint= Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a helpful assistant that can search through uploaded files to answer questions."; + +// We need the AIProjectClient to upload files and create vector stores. +AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); +var projectOpenAIClient = aiProjectClient.GetProjectOpenAIClient(); +var filesClient = projectOpenAIClient.GetProjectFilesClient(); +var vectorStoresClient = projectOpenAIClient.GetProjectVectorStoresClient(); + +// 1. Create a temp file with test content and upload it. +string searchFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "_lookup.txt"); +File.WriteAllText( + path: searchFilePath, + contents: """ + Employee Directory: + - Alice Johnson, 28 years old, Software Engineer, Engineering Department + - Bob Smith, 35 years old, Sales Manager, Sales Department + - Carol Williams, 42 years old, HR Director, Human Resources Department + - David Brown, 31 years old, Customer Support Lead, Support Department + """ +); + +Console.WriteLine($"Uploading file: {searchFilePath}"); +OpenAIFile uploadedFile = filesClient.UploadFile( + filePath: searchFilePath, + purpose: FileUploadPurpose.Assistants +); +Console.WriteLine($"Uploaded file, file ID: {uploadedFile.Id}"); + +// 2. Create a vector store with the uploaded file. +var vectorStoreResult = await vectorStoresClient.CreateVectorStoreAsync( + options: new() { FileIds = { uploadedFile.Id }, Name = "EmployeeDirectory_VectorStore" } +); +string vectorStoreId = vectorStoreResult.Value.Id; +Console.WriteLine($"Created vector store, vector store ID: {vectorStoreId}"); + +// Create a FoundryAgentClient with HostedFileSearchTool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: "FileSearchAgent-RAPI", + tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] }]); + +// Run the agent +Console.WriteLine("\n--- Running File Search Agent ---"); +AgentResponse response = await agent.RunAsync("Who is the youngest employee?"); +Console.WriteLine($"Response: {response}"); + +// Getting any file citation annotations generated by the tool +foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? [])) +{ + if (annotation.RawRepresentation is TextAnnotationUpdate citationAnnotation) + { + Console.WriteLine($$""" + File Citation: + File Id: {{citationAnnotation.OutputFileId}} + Text to Replace: {{citationAnnotation.TextToReplace}} + """); + } +} + +// Cleanup file resources (no agent cleanup needed - no server-side agent was created). +Console.WriteLine("\n--- Cleanup ---"); +await vectorStoresClient.DeleteVectorStoreAsync(vectorStoreId); +await filesClient.DeleteFileAsync(uploadedFile.Id); +File.Delete(searchFilePath); +Console.WriteLine("Cleanup completed successfully."); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/README.md new file mode 100644 index 0000000000..5752bb5bd3 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step16_FileSearch/README.md @@ -0,0 +1,29 @@ +# File Search with the Responses API + +This sample shows how to use the File Search tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Uploading files and creating vector stores via `AIProjectClient` +- Using `HostedFileSearchTool` with `FoundryAgentClient` +- Handling file citation annotations in agent responses +- Cleaning up file resources after use + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/FoundryAgentsRAPI_Step17_OpenAPITools.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/FoundryAgentsRAPI_Step17_OpenAPITools.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/FoundryAgentsRAPI_Step17_OpenAPITools.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/Program.cs new file mode 100644 index 0000000000..879851951c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/Program.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use OpenAPI Tools with a FoundryAgentClient using the Responses API directly. + +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI.AzureAI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a helpful assistant that can use the countries API to retrieve information about countries by their currency code."; + +// A simple OpenAPI specification for the REST Countries API +const string CountriesOpenApiSpec = """ +{ + "openapi": "3.1.0", + "info": { + "title": "REST Countries API", + "description": "Retrieve information about countries by currency code", + "version": "v3.1" + }, + "servers": [ + { + "url": "https://restcountries.com/v3.1" + } + ], + "paths": { + "/currency/{currency}": { + "get": { + "description": "Get countries that use a specific currency code (e.g., USD, EUR, GBP)", + "operationId": "GetCountriesByCurrency", + "parameters": [ + { + "name": "currency", + "in": "path", + "description": "Currency code (e.g., USD, EUR, GBP)", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response with list of countries", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + }, + "404": { + "description": "No countries found for the currency" + } + } + } + } + } +} +"""; + +// Create the OpenAPI function definition +var openApiFunction = new OpenAPIFunctionDefinition( + "get_countries", + BinaryData.FromString(CountriesOpenApiSpec), + new OpenAPIAnonymousAuthenticationDetails()) +{ + Description = "Retrieve information about countries by currency code" +}; + +// Create a FoundryAgentClient with OpenAPI tool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: "OpenAPIToolsAgent-RAPI", + tools: [((ResponseTool)AgentTool.CreateOpenApiTool(openApiFunction)).AsAITool()]); + +// Run the agent with a question about countries +Console.WriteLine(await agent.RunAsync("What countries use the Euro (EUR) as their currency? Please list them.")); diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/README.md new file mode 100644 index 0000000000..122a3cd966 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step17_OpenAPITools/README.md @@ -0,0 +1,29 @@ +# OpenAPI Tools with the Responses API + +This sample shows how to use OpenAPI tools with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Defining an OpenAPI specification inline +- Creating an `OpenAPIFunctionDefinition` for the REST Countries API +- Using `AgentTool.CreateOpenApiTool()` with `FoundryAgentClient` +- Server-side execution of OpenAPI tool calls + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/FoundryAgentsRAPI_Step18_BingCustomSearch.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/FoundryAgentsRAPI_Step18_BingCustomSearch.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/FoundryAgentsRAPI_Step18_BingCustomSearch.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/Program.cs new file mode 100644 index 0000000000..253c23c6ab --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/Program.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Bing Custom Search Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string connectionId = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID is not set."); +string instanceName = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME is not set."); + +const string AgentInstructions = """ + You are a helpful agent that can use Bing Custom Search tools to assist users. + Use the available Bing Custom Search tools to answer questions and perform tasks. + """; + +// Bing Custom Search tool parameters +BingCustomSearchToolParameters bingCustomSearchToolParameters = new([ + new BingCustomSearchConfiguration(connectionId, instanceName) +]); + +// Create a FoundryAgentClient with Bing Custom Search tool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: "BingCustomSearchAgent-RAPI", + tools: [((ResponseTool)AgentTool.CreateBingCustomSearchTool(bingCustomSearchToolParameters)).AsAITool()]); + +Console.WriteLine($"Created agent: {agent.Name}"); + +// Run the agent with a search query +AgentResponse response = await agent.RunAsync("Search for the latest news about Microsoft AI"); + +Console.WriteLine("\n=== Agent Response ==="); +foreach (var message in response.Messages) +{ + Console.WriteLine(message.Text); +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/README.md new file mode 100644 index 0000000000..abdfccab3c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step18_BingCustomSearch/README.md @@ -0,0 +1,36 @@ +# Bing Custom Search with the Responses API + +This sample shows how to use the Bing Custom Search tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Configuring `BingCustomSearchToolParameters` with connection ID and instance name +- Using `AgentTool.CreateBingCustomSearchTool()` with `FoundryAgentClient` +- Processing search results from agent responses + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) +- Bing Custom Search resource configured with a connection ID + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +$env:AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID="your-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/your-bing-connection" +$env:AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME="your-instance-name" # The Bing Custom Search configuration name (from Azure portal) +``` + +### Finding the connection ID and instance name + +- **Connection ID** (`AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID`): The full ARM resource URI including the `/projects//connections/` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**. +- **Instance Name** (`AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME`): The **configuration name** from your Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name or the connection name — it's the name of the specific search configuration that defines which domains/sites to search against. + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/FoundryAgentsRAPI_Step19_SharePoint.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/FoundryAgentsRAPI_Step19_SharePoint.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/FoundryAgentsRAPI_Step19_SharePoint.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/Program.cs new file mode 100644 index 0000000000..33eede8aaf --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/Program.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use SharePoint Grounding Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string sharepointConnectionId = Environment.GetEnvironmentVariable("SHAREPOINT_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("SHAREPOINT_PROJECT_CONNECTION_ID is not set."); + +const string AgentInstructions = """ + You are a helpful agent that can use SharePoint tools to assist users. + Use the available SharePoint tools to answer questions and perform tasks. + """; + +// Create SharePoint tool options with project connection +var sharepointOptions = new SharePointGroundingToolOptions(); +sharepointOptions.ProjectConnections.Add(new ToolProjectConnection(sharepointConnectionId)); + +// Create a FoundryAgentClient with SharePoint tool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: "SharePointAgent-RAPI", + tools: [((ResponseTool)AgentTool.CreateSharepointTool(sharepointOptions)).AsAITool()]); + +Console.WriteLine($"Created agent: {agent.Name}"); + +AgentResponse response = await agent.RunAsync("List the documents available in SharePoint"); + +// Display the response +Console.WriteLine("\n=== Agent Response ==="); +Console.WriteLine(response); + +// Display grounding annotations if any +foreach (var message in response.Messages) +{ + foreach (var content in message.Contents) + { + if (content.Annotations is not null) + { + foreach (var annotation in content.Annotations) + { + Console.WriteLine($"Annotation: {annotation}"); + } + } + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/README.md new file mode 100644 index 0000000000..d64cfe5709 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step19_SharePoint/README.md @@ -0,0 +1,30 @@ +# SharePoint Grounding with the Responses API + +This sample shows how to use the SharePoint Grounding tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Configuring `SharePointGroundingToolOptions` with project connections +- Using `AgentTool.CreateSharepointTool()` with `FoundryAgentClient` +- Displaying grounding annotations from agent responses + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) +- SharePoint connection configured in your Azure Foundry project + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/SharepointTestTool" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/FoundryAgentsRAPI_Step20_MicrosoftFabric.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/FoundryAgentsRAPI_Step20_MicrosoftFabric.csproj new file mode 100644 index 0000000000..0cdfd801ed --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/FoundryAgentsRAPI_Step20_MicrosoftFabric.csproj @@ -0,0 +1,20 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/Program.cs new file mode 100644 index 0000000000..8e1b82b908 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/Program.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Microsoft Fabric Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set."); + +const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection."; + +// Configure Microsoft Fabric tool options with project connection +var fabricToolOptions = new FabricDataAgentToolOptions(); +fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection(fabricConnectionId)); + +// Create a FoundryAgentClient with Microsoft Fabric tool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: "FabricAgent-RAPI", + tools: [((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions)).AsAITool()]); + +Console.WriteLine($"Created agent: {agent.Name}"); + +// Run the agent with a sample query +AgentResponse response = await agent.RunAsync("What data is available in the connected Fabric workspace?"); + +Console.WriteLine("\n=== Agent Response ==="); +foreach (var message in response.Messages) +{ + Console.WriteLine(message.Text); +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/README.md new file mode 100644 index 0000000000..a88762219d --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step20_MicrosoftFabric/README.md @@ -0,0 +1,30 @@ +# Microsoft Fabric with the Responses API + +This sample shows how to use the Microsoft Fabric tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Configuring `FabricDataAgentToolOptions` with project connections +- Using `AgentTool.CreateMicrosoftFabricTool()` with `FoundryAgentClient` +- Querying data available through a Fabric connection + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) +- Microsoft Fabric connection configured in your Azure Foundry project + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/FabricTestTool" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/FoundryAgentsRAPI_Step21_WebSearch.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/FoundryAgentsRAPI_Step21_WebSearch.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/FoundryAgentsRAPI_Step21_WebSearch.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/Program.cs new file mode 100644 index 0000000000..aa8f70a462 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/Program.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use the Web Search Tool with a FoundryAgentClient using the Responses API directly. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a helpful assistant that can search the web to find current information and answer questions accurately."; +const string AgentName = "WebSearchAgent-RAPI"; + +// Create a FoundryAgentClient with HostedWebSearchTool using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: AgentName, + tools: [new HostedWebSearchTool()]); + +AgentResponse response = await agent.RunAsync("What's the weather today in Seattle?"); + +// Get the text response +Console.WriteLine($"Response: {response.Text}"); + +// Getting any annotations/citations generated by the web search tool +foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? [])) +{ + Console.WriteLine($"Annotation: {annotation}"); + if (annotation.RawRepresentation is UriCitationMessageAnnotation urlCitation) + { + Console.WriteLine($$""" + Title: {{urlCitation.Title}} + URL: {{urlCitation.Uri}} + """); + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/README.md new file mode 100644 index 0000000000..f12697e7c5 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step21_WebSearch/README.md @@ -0,0 +1,28 @@ +# Web Search with the Responses API + +This sample shows how to use the Web Search tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Using `HostedWebSearchTool` with `FoundryAgentClient` +- Processing web search citations and annotations +- Extracting URL citation details (title, URL) from responses + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/FoundryAgentsRAPI_Step22_MemorySearch.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/FoundryAgentsRAPI_Step22_MemorySearch.csproj new file mode 100644 index 0000000000..c6c12051e1 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/FoundryAgentsRAPI_Step22_MemorySearch.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/Program.cs new file mode 100644 index 0000000000..49284fa3a5 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/Program.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use the Memory Search Tool with a FoundryAgentClient using the Responses API directly. +// Memories are explicitly stored first via the MemoryStores API, then the MemorySearchPreviewTool +// is used by the agent to retrieve them during conversation. + +using Azure.AI.Projects; +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002"; +string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? $"rapi-memory-sample-{Guid.NewGuid():N}"; + +const string AgentName = "MemorySearchAgent-RAPI"; +string memoryScope = "travel-preferences"; + +DefaultAzureCredential credential = new(); +AIProjectClient aiProjectClient = new(new Uri(endpoint), credential); + +// Ensure the memory store exists and has memories to retrieve. +await EnsureMemoryStoreAsync(); + +try +{ + // Create a FoundryAgentClient with the Memory Search tool. + // The tool retrieves memories — it does NOT store new ones during conversation. + MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, memoryScope) { UpdateDelay = 0 }; + + FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: credential, + model: deploymentName, + instructions: "You are a helpful travel assistant. Use the memory search tool to recall what you know about the user from past conversations.", + name: AgentName, + tools: [((ResponseTool)memorySearchTool).AsAITool()]); + + Console.WriteLine("Agent created. Asking about previously stored memories...\n"); + + // The agent uses the memory search tool to recall stored information. + Console.WriteLine("User: What do you remember about my upcoming trip?"); + AgentResponse response = await agent.RunAsync("What do you remember about my upcoming trip?"); + Console.WriteLine($"Agent: {response.Messages.LastOrDefault()?.Text}\n"); + + // Inspect memory search results if available in raw response items. + foreach (var message in response.Messages) + { + if (message.RawRepresentation is MemorySearchToolCallResponseItem memorySearchResult) + { + Console.WriteLine($"Memory Search Status: {memorySearchResult.Status}"); + Console.WriteLine($"Memory Search Results Count: {memorySearchResult.Results.Count}"); + + foreach (var result in memorySearchResult.Results) + { + var memoryItem = result.MemoryItem; + Console.WriteLine($" - Memory ID: {memoryItem.MemoryId}"); + Console.WriteLine($" Scope: {memoryItem.Scope}"); + Console.WriteLine($" Content: {memoryItem.Content}"); + Console.WriteLine($" Updated: {memoryItem.UpdatedAt}"); + } + } + } +} +finally +{ + // Cleanup: Delete the memory store. + Console.WriteLine($"\nCleaning up memory store '{memoryStoreName}'..."); + await aiProjectClient.MemoryStores.DeleteMemoryStoreAsync(memoryStoreName); + Console.WriteLine("Memory store deleted."); +} + +// This creates a temporary memory to demonstrate the memory search functionality. +// In normal usage, the memories should be already present to be used by the tool. +async Task EnsureMemoryStoreAsync() +{ + // Create the store if it doesn't already exist. + Console.WriteLine($"Creating memory store '{memoryStoreName}'..."); + try + { + await aiProjectClient.MemoryStores.GetMemoryStoreAsync(memoryStoreName); + Console.WriteLine("Memory store already exists."); + } + catch (System.ClientModel.ClientResultException ex) when (ex.Status == 404) + { + MemoryStoreDefaultDefinition definition = new(deploymentName, embeddingModelName); + await aiProjectClient.MemoryStores.CreateMemoryStoreAsync(memoryStoreName, definition, "Sample memory store for RAPI Memory Search demo"); + Console.WriteLine("Memory store created."); + } + + // Explicitly add memories from a simulated prior conversation. + Console.WriteLine("Storing memories from a prior conversation..."); + MemoryUpdateOptions memoryOptions = new(memoryScope) { UpdateDelay = 0 }; + memoryOptions.Items.Add(ResponseItem.CreateUserMessageItem("My name is Alice and I'm planning a trip to Japan next spring.")); + + MemoryUpdateResult updateResult = await aiProjectClient.MemoryStores.WaitForMemoriesUpdateAsync( + memoryStoreName: memoryStoreName, + options: memoryOptions, + pollingInterval: 500); + + if (updateResult.Status == MemoryStoreUpdateStatus.Failed) + { + throw new InvalidOperationException($"Memory update failed: {updateResult.ErrorDetails}"); + } + + Console.WriteLine($"Memory update completed (status: {updateResult.Status})."); + + // Quick verification that memories are searchable. + Console.WriteLine("Verifying stored memories..."); + MemorySearchOptions searchOptions = new(memoryScope) + { + Items = { ResponseItem.CreateUserMessageItem("What are Alice's travel preferences?") } + }; + MemoryStoreSearchResponse searchResult = await aiProjectClient.MemoryStores.SearchMemoriesAsync( + memoryStoreName: memoryStoreName, + options: searchOptions); + + foreach (var memory in searchResult.Memories) + { + Console.WriteLine($" - {memory.MemoryItem.Content}"); + } + + Console.WriteLine(); +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/README.md new file mode 100644 index 0000000000..6ddd51f773 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step22_MemorySearch/README.md @@ -0,0 +1,31 @@ +# Memory Search with the Responses API + +This sample demonstrates how to use the Memory Search tool with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Configuring `MemorySearchPreviewTool` with a memory store and user scope +- Using memory search for cross-conversation recall +- Inspecting `MemorySearchToolCallResponseItem` results +- User profile persistence across conversations + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) +- A memory store created beforehand via Azure Portal or Python SDK + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +$env:AZURE_AI_MEMORY_STORE_ID="your-memory-store-name" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/FoundryAgentsRAPI_Step23_LocalMCP.csproj b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/FoundryAgentsRAPI_Step23_LocalMCP.csproj new file mode 100644 index 0000000000..a69c81f08c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/FoundryAgentsRAPI_Step23_LocalMCP.csproj @@ -0,0 +1,20 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/Program.cs b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/Program.cs new file mode 100644 index 0000000000..54497c1f1c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/Program.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use a local MCP (Model Context Protocol) client with a FoundryAgentClient +// using the Responses API directly. The MCP tools are resolved locally by connecting directly to the MCP +// server via HTTP, and then passed to the agent as client-side tools. + +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.AzureAI; +using Microsoft.Extensions.AI; +using ModelContextProtocol.Client; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation."; +const string AgentName = "DocsAgent-RAPI"; + +// Connect to the MCP server locally via HTTP (Streamable HTTP transport). +Console.WriteLine("Connecting to MCP server at https://learn.microsoft.com/api/mcp ..."); + +await using McpClient mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new() +{ + Endpoint = new Uri("https://learn.microsoft.com/api/mcp"), + Name = "Microsoft Learn MCP", +})); + +// Retrieve the list of tools available on the MCP server (resolved locally). +IList mcpTools = await mcpClient.ListToolsAsync(); +Console.WriteLine($"MCP tools available: {string.Join(", ", mcpTools.Select(t => t.Name))}"); + +// Wrap each MCP tool with a DelegatingAIFunction to log local invocations. +List wrappedTools = mcpTools.Select(tool => (AITool)new LoggingMcpTool(tool)).ToList(); + +// Create a FoundryAgentClient with the locally-resolved MCP tools using the Responses API directly. +// No server-side agent is created. +FoundryAgentClient agent = new( + endpoint: new Uri(endpoint), + tokenProvider: new DefaultAzureCredential(), + model: deploymentName, + instructions: AgentInstructions, + name: AgentName, + tools: wrappedTools); + +Console.WriteLine($"Agent '{agent.Name}' created successfully."); + +// First query +const string Prompt1 = "How does one create an Azure storage account using az cli?"; +Console.WriteLine($"\nUser: {Prompt1}\n"); +AgentResponse response1 = await agent.RunAsync(Prompt1); +Console.WriteLine($"Agent: {response1}"); + +Console.WriteLine("\n=======================================\n"); + +// Second query +const string Prompt2 = "What is Microsoft Agent Framework?"; +Console.WriteLine($"User: {Prompt2}\n"); +AgentResponse response2 = await agent.RunAsync(Prompt2); +Console.WriteLine($"Agent: {response2}"); + +/// +/// Wraps an MCP tool to log when it is invoked locally, +/// confirming that the MCP call is happening client-side. +/// +internal sealed class LoggingMcpTool(AIFunction innerFunction) : DelegatingAIFunction(innerFunction) +{ + protected override ValueTask InvokeCoreAsync(AIFunctionArguments arguments, CancellationToken cancellationToken) + { + Console.WriteLine($" >> [LOCAL MCP] Invoking tool '{this.Name}' locally..."); + return base.InvokeCoreAsync(arguments, cancellationToken); + } +} diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/README.md new file mode 100644 index 0000000000..ec55c136e5 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/FoundryAgentsRAPI_Step23_LocalMCP/README.md @@ -0,0 +1,29 @@ +# Local MCP with the Responses API + +This sample demonstrates how to use a local MCP (Model Context Protocol) client with a `FoundryAgentClient` using the Responses API directly. + +## What this sample demonstrates + +- Connecting to an MCP server via HTTP (Streamable HTTP transport) +- Resolving MCP tools locally and wrapping them with logging +- Using `DelegatingAIFunction` to add custom behavior to MCP tools +- Passing locally-resolved MCP tools to `FoundryAgentClient` + +## Prerequisites + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (`az login`) + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +## Run the sample + +```powershell +dotnet run +``` diff --git a/dotnet/samples/02-agents/FoundryAgents-RAPI/README.md b/dotnet/samples/02-agents/FoundryAgents-RAPI/README.md new file mode 100644 index 0000000000..f05aeb17a6 --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents-RAPI/README.md @@ -0,0 +1,98 @@ +# Getting started with Foundry Agents using the Responses API + +These samples demonstrate how to use the `FoundryAgentClient` to work with Azure AI Foundry +using the Responses API directly, without creating server-side agent definitions. + +Unlike the standard [Foundry Agents](../FoundryAgents/README.md) samples, which create and manage +server-side agents with versioned definitions, the Responses API (RAPI) approach uses the +`FoundryAgentClient` to send requests directly to the Responses API. This means: + +- **No server-side agent creation**: Instructions, tools, and options are provided locally at construction time. +- **No agent versioning**: The agent behavior is defined entirely in code. +- **Simpler lifecycle**: No need to create or delete agents in the Foundry service. + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and project configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: These samples use Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Replace with your model deployment name +``` + +The `FoundryAgentClient` auto-discovers these environment variables at construction time, so no endpoint or credential code is needed in the samples. + +## Samples + +|Sample|Description| +|---|---| +|[Basics](./FoundryAgentsRAPI_Step01_Basics/)|This sample demonstrates how to create and run a basic agent using the Responses API| +|[Multi-turn conversation](./FoundryAgentsRAPI_Step02_MultiturnConversation/)|This sample demonstrates how to implement a multi-turn conversation using the Responses API| +|[Using function tools](./FoundryAgentsRAPI_Step03_UsingFunctionTools/)|This sample demonstrates how to use function tools with the Responses API| +|[Using function tools with approvals](./FoundryAgentsRAPI_Step04_UsingFunctionToolsWithApprovals/)|This sample demonstrates how to use function tools with human-in-the-loop approval| +|[Structured output](./FoundryAgentsRAPI_Step05_StructuredOutput/)|This sample demonstrates how to use structured output with the Responses API| +|[Persisted conversations](./FoundryAgentsRAPI_Step06_PersistedConversations/)|This sample demonstrates how to persist and resume conversations| +|[Observability](./FoundryAgentsRAPI_Step07_Observability/)|This sample demonstrates how to add OpenTelemetry observability| +|[Dependency injection](./FoundryAgentsRAPI_Step08_DependencyInjection/)|This sample demonstrates how to use dependency injection with a hosted service| +|[Using images](./FoundryAgentsRAPI_Step10_UsingImages/)|This sample demonstrates how to use image multi-modality| +|[Agent as function tool](./FoundryAgentsRAPI_Step11_AsFunctionTool/)|This sample demonstrates how to use one agent as a function tool for another| +|[Middleware](./FoundryAgentsRAPI_Step12_Middleware/)|This sample demonstrates multiple middleware layers (PII, guardrails, approvals)| +|[Using MCP client as tools](./FoundryAgentsRAPI_Step09_UsingMcpClientAsTools/)|This sample demonstrates how to use MCP client tools with the Responses API| +|[Plugins](./FoundryAgentsRAPI_Step13_Plugins/)|This sample demonstrates how to use plugins with dependency injection| +|[Code interpreter](./FoundryAgentsRAPI_Step14_CodeInterpreter/)|This sample demonstrates how to use the code interpreter tool| +|[Computer use](./FoundryAgentsRAPI_Step15_ComputerUse/)|This sample demonstrates how to use the computer use tool| +|[File search](./FoundryAgentsRAPI_Step16_FileSearch/)|This sample demonstrates how to use the file search tool| +|[OpenAPI tools](./FoundryAgentsRAPI_Step17_OpenAPITools/)|This sample demonstrates how to use OpenAPI tools| +|[Bing custom search](./FoundryAgentsRAPI_Step18_BingCustomSearch/)|This sample demonstrates how to use the Bing Custom Search tool| +|[SharePoint](./FoundryAgentsRAPI_Step19_SharePoint/)|This sample demonstrates how to use the SharePoint grounding tool| +|[Microsoft Fabric](./FoundryAgentsRAPI_Step20_MicrosoftFabric/)|This sample demonstrates how to use the Microsoft Fabric tool| +|[Web search](./FoundryAgentsRAPI_Step21_WebSearch/)|This sample demonstrates how to use the web search tool| +|[Memory search](./FoundryAgentsRAPI_Step22_MemorySearch/)|This sample demonstrates how to use the memory search tool| +|[Local MCP](./FoundryAgentsRAPI_Step23_LocalMCP/)|This sample demonstrates how to use a local MCP client with HTTP transport| + +## Running the samples from the console + +To run the samples, navigate to the desired sample directory, e.g. + +```powershell +cd FoundryAgentsRAPI_Step01_Basics +``` + +Ensure the following environment variables are set: + +```powershell +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +``` + +Execute the following command to build the sample: + +```powershell +dotnet build +``` + +Execute the following command to run the sample: + +```powershell +dotnet run --no-build +``` + +Or just build and run in one step: + +```powershell +dotnet run +``` + +## Running the samples from Visual Studio + +Open the solution in Visual Studio and set the desired sample project as the startup project. Then, run the project using the built-in debugger or by pressing `F5`. + +You will be prompted for any required environment variables if they are not already set. diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md index ccc1873a04..7b64c220fe 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md @@ -33,16 +33,16 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini -$env:BING_CUSTOM_SEARCH_PROJECT_CONNECTION_ID="/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/" -$env:BING_CUSTOM_SEARCH_INSTANCE_NAME="your-configuration-name" +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID="/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/" +$env:AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME="your-configuration-name" ``` ### Finding the connection ID and instance name -- **Connection ID**: The full ARM resource path including the `/projects//connections/` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**. -- **Instance Name**: The **configuration name** from the Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name. +- **Connection ID** (`AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID`): The full ARM resource URI including the `/projects//connections/` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**. +- **Instance Name** (`AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME`): The **configuration name** from your Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name or the connection name — it's the name of the specific search configuration that defines which domains/sites to search against. ## Run the sample diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs index 6d1daf85df..26747b89e4 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs @@ -8,8 +8,8 @@ using Microsoft.Agents.AI; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; string sharepointConnectionId = Environment.GetEnvironmentVariable("SHAREPOINT_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("SHAREPOINT_PROJECT_CONNECTION_ID is not set."); const string AgentInstructions = """ diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md index ccbd699011..2172edbf05 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md @@ -23,9 +23,9 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini -$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # Required: SharePoint project connection ID +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # Required: The full ARM resource URI, e.g., "/subscriptions/.../connections/SharepointTestTool" ``` ## Run the sample diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs index 2f13c2c30c..3f4f0b319b 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs @@ -8,8 +8,8 @@ using Microsoft.Agents.AI; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set."); const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection."; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md index a5faf79d9d..6c68984ca2 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md @@ -32,9 +32,9 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini -$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The Fabric project connection ID from Azure Foundry +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/FabricTestTool" ``` ## Run the sample diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md index 8da390878c..2c00048a4e 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md @@ -25,8 +25,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs index 836bf1b684..bce0ce0d78 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs @@ -12,11 +12,8 @@ string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; - -// Memory store configuration -// NOTE: Memory stores must be created beforehand via Azure Portal or Python SDK. -// The .NET SDK currently only supports using existing memory stores with agents. -string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? throw new InvalidOperationException("AZURE_AI_MEMORY_STORE_ID is not set."); +string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002"; +string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? $"foundry-memory-sample-{Guid.NewGuid():N}"; const string AgentInstructions = """ You are a helpful assistant that remembers past conversations. @@ -27,76 +24,91 @@ Use the memory search tool to recall relevant information from previous interact const string AgentNameMEAI = "MemorySearchAgent-MEAI"; const string AgentNameNative = "MemorySearchAgent-NATIVE"; -// Scope identifies the user or context for memory isolation. -// Using a unique user identifier ensures memories are private to that user. string userScope = $"user_{Environment.MachineName}"; -// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents. -AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential()); +DefaultAzureCredential credential = new(); +AIProjectClient aiProjectClient = new(new Uri(endpoint), credential); -// Create the Memory Search tool configuration -MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) -{ - // Optional: Configure how quickly new memories are indexed (in seconds) - UpdateDelay = 1, +// Ensure the memory store exists and has memories to retrieve. +await EnsureMemoryStoreAsync(); - // Optional: Configure search behavior - SearchOptions = new MemorySearchToolOptions - { - // Additional search options can be configured here if needed - } -}; +MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) { UpdateDelay = 0 }; // Create agent using Option 1 (MEAI) or Option 2 (Native SDK) AIAgent agent = await CreateAgentWithMEAI(); // AIAgent agent = await CreateAgentWithNativeSDK(); -Console.WriteLine("Agent created with Memory Search tool. Starting conversation...\n"); - -// Conversation 1: Share some personal information -Console.WriteLine("User: My name is Alice and I love programming in C#."); -AgentResponse response1 = await agent.RunAsync("My name is Alice and I love programming in C#."); -Console.WriteLine($"Agent: {response1.Messages.LastOrDefault()?.Text}\n"); - -// Allow time for memory to be indexed -await Task.Delay(2000); +try +{ + Console.WriteLine("Agent created with Memory Search tool. Starting conversation...\n"); -// Conversation 2: Test if the agent remembers -Console.WriteLine("User: What's my name and what programming language do I prefer?"); -AgentResponse response2 = await agent.RunAsync("What's my name and what programming language do I prefer?"); -Console.WriteLine($"Agent: {response2.Messages.LastOrDefault()?.Text}\n"); + // The agent uses the memory search tool to recall stored information. + Console.WriteLine("User: What's my name and what programming language do I prefer?"); + AgentResponse response = await agent.RunAsync("What's my name and what programming language do I prefer?"); + Console.WriteLine($"Agent: {response.Messages.LastOrDefault()?.Text}\n"); -// Inspect memory search results if available in raw response items -// Note: Memory search tool call results appear as AgentResponseItem types -foreach (var message in response2.Messages) -{ - if (message.RawRepresentation is AgentResponseItem agentResponseItem && - agentResponseItem is MemorySearchToolCallResponseItem memorySearchResult) + // Inspect memory search results if available in raw response items. + foreach (var message in response.Messages) { - Console.WriteLine($"Memory Search Status: {memorySearchResult.Status}"); - Console.WriteLine($"Memory Search Results Count: {memorySearchResult.Results.Count}"); - - foreach (var result in memorySearchResult.Results) + if (message.RawRepresentation is MemorySearchToolCallResponseItem memorySearchResult) { - var memoryItem = result.MemoryItem; - Console.WriteLine($" - Memory ID: {memoryItem.MemoryId}"); - Console.WriteLine($" Scope: {memoryItem.Scope}"); - Console.WriteLine($" Content: {memoryItem.Content}"); - Console.WriteLine($" Updated: {memoryItem.UpdatedAt}"); + Console.WriteLine($"Memory Search Status: {memorySearchResult.Status}"); + Console.WriteLine($"Memory Search Results Count: {memorySearchResult.Results.Count}"); + + foreach (var result in memorySearchResult.Results) + { + var memoryItem = result.MemoryItem; + Console.WriteLine($" - Memory ID: {memoryItem.MemoryId}"); + Console.WriteLine($" Scope: {memoryItem.Scope}"); + Console.WriteLine($" Content: {memoryItem.Content}"); + Console.WriteLine($" Updated: {memoryItem.UpdatedAt}"); + } } } } +finally +{ + // Cleanup: Delete the agent and memory store. + Console.WriteLine("\nCleaning up..."); + await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); + Console.WriteLine("Agent deleted."); + await aiProjectClient.MemoryStores.DeleteMemoryStoreAsync(memoryStoreName); + Console.WriteLine("Memory store deleted."); +} + +// Helpers — kept at the bottom so the main agent flow above stays clean. +async Task EnsureMemoryStoreAsync() +{ + Console.WriteLine($"Creating memory store '{memoryStoreName}'..."); + try + { + await aiProjectClient.MemoryStores.GetMemoryStoreAsync(memoryStoreName); + Console.WriteLine("Memory store already exists."); + } + catch (System.ClientModel.ClientResultException ex) when (ex.Status == 404) + { + MemoryStoreDefaultDefinition definition = new(deploymentName, embeddingModelName); + await aiProjectClient.MemoryStores.CreateMemoryStoreAsync(memoryStoreName, definition, "Sample memory store for Memory Search demo"); + Console.WriteLine("Memory store created."); + } + + Console.WriteLine("Storing memories from a prior conversation..."); + MemoryUpdateOptions memoryOptions = new(userScope) { UpdateDelay = 0 }; + memoryOptions.Items.Add(ResponseItem.CreateUserMessageItem("My name is Alice and I love programming in C#.")); + + MemoryUpdateResult updateResult = await aiProjectClient.MemoryStores.WaitForMemoriesUpdateAsync( + memoryStoreName: memoryStoreName, + options: memoryOptions, + pollingInterval: 500); -// Cleanup: Delete the agent (memory store persists and should be cleaned up separately if needed) -Console.WriteLine("\nCleaning up agent..."); -await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); -Console.WriteLine("Agent deleted successfully."); + if (updateResult.Status == MemoryStoreUpdateStatus.Failed) + { + throw new InvalidOperationException($"Memory update failed: {updateResult.ErrorDetails}"); + } -// NOTE: Memory stores are long-lived resources and are NOT deleted with the agent. -// To delete a memory store, use the Azure Portal or Python SDK: -// await project_client.memory_stores.delete(memory_store.name) + Console.WriteLine($"Memory update completed (status: {updateResult.Status}).\n"); +} -// --- Agent Creation Options --- #pragma warning disable CS8321 // Local function is declared but never used // Option 1 - Using MemorySearchTool wrapped as MEAI AITool diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md index 9e6d79d579..b0ec93483d 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md @@ -60,8 +60,8 @@ memory_store = await project_client.memory_stores.create( Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini $env:AZURE_AI_MEMORY_STORE_NAME="your-memory-store-name" # Required - name of pre-created memory store ``` diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgentClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgentClient.cs new file mode 100644 index 0000000000..6a9b36b604 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgentClient.cs @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using Azure.AI.Projects; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Logging; +using Microsoft.Shared.DiagnosticIds; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Agents.AI.AzureAI; + +/// +/// Provides an that uses an Azure AI Foundry project as its backing service. +/// +/// +/// This agent internally creates an and a +/// backed by the project's Responses API. All operations are delegated to the internal . +/// +[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)] +public sealed class FoundryAgentClient : AIAgent +{ + private const string ProjectEndpointEnvVar = "AZURE_AI_PROJECT_ENDPOINT"; + private const string ModelDeploymentEnvVar = "AZURE_AI_MODEL_DEPLOYMENT_NAME"; + + private readonly AIProjectClient _aiProjectClient; + private readonly ChatClientAgent _innerAgent; + + /// + /// Initializes a new instance of the class using environment variables for configuration. + /// + /// Optional system instructions that guide the agent's behavior. + /// Optional name for the agent. + /// Optional human-readable description of the agent's purpose and capabilities. + /// Optional collection of tools that the agent can invoke during conversations. + /// Provides a way to customize the creation of the underlying used by the agent. + /// Optional logger factory for creating loggers used by the agent. + /// Optional service provider for resolving dependencies required by AI functions. + /// The AZURE_AI_PROJECT_ENDPOINT environment variable is not set. + /// + /// + /// This constructor reads the following environment variables: + /// + /// AZURE_AI_PROJECT_ENDPOINT (required) — The Azure AI Foundry project endpoint URL. + /// AZURE_AI_MODEL_DEPLOYMENT_NAME (optional) — The model deployment name to use. + /// + /// + /// Authentication uses . + /// + public FoundryAgentClient( + string? instructions = null, + string? name = null, + string? description = null, + IList? tools = null, + Func? chatClientFactory = null, + ILoggerFactory? loggerFactory = null, + IServiceProvider? services = null) + : this( + new Uri(Environment.GetEnvironmentVariable(ProjectEndpointEnvVar) + ?? throw new InvalidOperationException($"Environment variable '{ProjectEndpointEnvVar}' is not set.")), + new DefaultAzureCredential(), + Environment.GetEnvironmentVariable(ModelDeploymentEnvVar) ?? string.Empty, + clientOptions: null, + instructions, + name, + description, + tools, + chatClientFactory, + loggerFactory, + services) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The Azure AI Foundry project endpoint. + /// The authentication token provider used to authenticate with the Azure AI Foundry service. + /// The model deployment name to use for the agent (e.g., "gpt-4o-mini"). + /// Optional configuration options for the . + /// Optional system instructions that guide the agent's behavior. + /// Optional name for the agent. + /// Optional human-readable description of the agent's purpose and capabilities. + /// Optional collection of tools that the agent can invoke during conversations. + /// Provides a way to customize the creation of the underlying used by the agent. + /// Optional logger factory for creating loggers used by the agent. + /// Optional service provider for resolving dependencies required by AI functions. + /// or is . + public FoundryAgentClient( + Uri endpoint, + AuthenticationTokenProvider tokenProvider, + string model, + AIProjectClientOptions? clientOptions = null, + string? instructions = null, + string? name = null, + string? description = null, + IList? tools = null, + Func? chatClientFactory = null, + ILoggerFactory? loggerFactory = null, + IServiceProvider? services = null) + : this( + endpoint, + tokenProvider, + clientOptions, + new ChatClientAgentOptions + { + ChatOptions = new ChatOptions + { + ModelId = model, + Tools = tools, + Instructions = instructions + }, + Name = name, + Description = description + }, + chatClientFactory, + loggerFactory, + services) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The Azure AI Foundry project endpoint. + /// The authentication token provider used to authenticate with the Azure AI Foundry service. + /// Optional configuration options for the . + /// Configuration options that control all aspects of the agent's behavior. + /// Provides a way to customize the creation of the underlying used by the agent. + /// Optional logger factory for creating loggers used by the agent. + /// Optional service provider for resolving dependencies required by AI functions. + /// or is . + public FoundryAgentClient( + Uri endpoint, + AuthenticationTokenProvider tokenProvider, + AIProjectClientOptions? clientOptions = null, + ChatClientAgentOptions? options = null, + Func? chatClientFactory = null, + ILoggerFactory? loggerFactory = null, + IServiceProvider? services = null) + { + Throw.IfNull(endpoint); + Throw.IfNull(tokenProvider); + + clientOptions ??= new AIProjectClientOptions(); + clientOptions.AddPolicy(RequestOptionsExtensions.UserAgentPolicy, PipelinePosition.PerCall); + + this._aiProjectClient = new AIProjectClient(endpoint, tokenProvider, clientOptions); + + IChatClient chatClient = this._aiProjectClient + .OpenAI + .GetProjectResponsesClientForModel(options?.ChatOptions?.ModelId ?? string.Empty) + .AsIChatClient(); + + if (chatClientFactory is not null) + { + chatClient = chatClientFactory(chatClient); + } + + this._innerAgent = new ChatClientAgent(chatClient, options, loggerFactory, services); + } + + /// + protected override string? IdCore => this._innerAgent.Id; + + /// + public override string? Name => this._innerAgent.Name; + + /// + public override string? Description => this._innerAgent.Description; + + /// + public override object? GetService(Type serviceType, object? serviceKey = null) + { + return base.GetService(serviceType, serviceKey) + ?? (serviceKey is null && serviceType == typeof(AIProjectClient) ? this._aiProjectClient + : serviceKey is null && serviceType == typeof(ChatClientAgent) ? this._innerAgent + : this._innerAgent.GetService(serviceType, serviceKey)); + } + + /// + protected override Task RunCoreAsync( + IEnumerable messages, + AgentSession? session = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + => this._innerAgent.RunAsync(messages, session, options, cancellationToken); + + /// + protected override IAsyncEnumerable RunCoreStreamingAsync( + IEnumerable messages, + AgentSession? session = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + => this._innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); + + /// + protected override ValueTask CreateSessionCoreAsync(CancellationToken cancellationToken = default) + => this._innerAgent.CreateSessionAsync(cancellationToken); + + /// + protected override ValueTask SerializeSessionCoreAsync(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => this._innerAgent.SerializeSessionAsync(session, jsonSerializerOptions, cancellationToken); + + /// + protected override ValueTask DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => this._innerAgent.DeserializeSessionAsync(serializedState, jsonSerializerOptions, cancellationToken); +} diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs index 722d316330..2705611b57 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs @@ -7,6 +7,9 @@ namespace Microsoft.Agents.AI; internal static class RequestOptionsExtensions { + /// Gets the singleton that adds a MEAI user-agent header. + internal static PipelinePolicy UserAgentPolicy => MeaiUserAgentPolicy.Instance; + /// Creates a configured for use with Foundry Agents. public static RequestOptions ToRequestOptions(this CancellationToken cancellationToken, bool streaming) { diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs index 94ce01e221..d2c230a5a0 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Threading.Tasks; using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs index 778a6dd7b7..7fd79dca18 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks;