Skip to content

Commit 8515c0c

Browse files
.NET: Add Anthropic Agent Package (microsoft#2359)
* WIP * WIP * Simple call working * Update Thinking sample * Non-Streaming Function calling working * Update Anthropic Impl * Public Preps * UT + IT working * Update documentation + samples * Update variable * Revert nuget.config * Add IT for BetaService implementation * Remove polyfill + enable IT to run for netstandard 2.0 * Skipping Anthropic IT's for manual execution and avoid pipeline execution * Fix compilation error * Address error in UT * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix warning * Net 10 update * Update for NET 10, remove Anthropic.Foundry due to vulnerability * Final missing adjustments for NET 10 * Address PR comments * Remove unused code * Address feedback --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 1b41ebd commit 8515c0c

33 files changed

Lines changed: 1628 additions & 2 deletions

dotnet/Directory.Packages.props

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
</PropertyGroup>
1212
<ItemGroup>
1313
<!-- Aspire.* -->
14+
<PackageVersion Include="Anthropic" Version="10.2.1" />
15+
<PackageVersion Include="Anthropic.Foundry" Version="0.0.2" />
1416
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="13.0.0-preview.1.25560.3" />
1517
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireAppHostSdkVersion)" />
1618
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="$(AspireAppHostSdkVersion)" />
@@ -169,4 +171,4 @@
169171
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
170172
</PackageReference>
171173
</ItemGroup>
172-
</Project>
174+
</Project>

dotnet/agent-framework-dotnet.slnx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<Folder Name="/Samples/GettingStarted/AgentProviders/">
4646
<File Path="samples/GettingStarted/AgentProviders/README.md" />
4747
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj" />
48+
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj" />
4849
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj" />
4950
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj" />
5051
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj" />
@@ -82,6 +83,12 @@
8283
<File Path="samples/GettingStarted/DevUI/README.md" />
8384
<Project Path="samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj" />
8485
</Folder>
86+
<Folder Name="/Samples/GettingStarted/AgentWithAnthropic/">
87+
<File Path="samples/GettingStarted/AgentWithAnthropic/README.md" />
88+
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj" />
89+
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj" />
90+
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj" />
91+
</Folder>
8592
<Folder Name="/Samples/GettingStarted/AgentWithMemory/">
8693
<File Path="samples/GettingStarted/AgentWithMemory/README.md" />
8794
<Project Path="samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj" />
@@ -312,6 +319,7 @@
312319
<File Path="src/Shared/Demos/SampleEnvironment.cs" />
313320
</Folder>
314321
<Folder Name="/Solution Items/src/Shared/IntegrationTests/">
322+
<File Path="src/Shared/IntegrationTests/AnthropicConfiguration.cs" />
315323
<File Path="src/Shared/IntegrationTests/AzureAIConfiguration.cs" />
316324
<File Path="src/Shared/IntegrationTests/Mem0Configuration.cs" />
317325
<File Path="src/Shared/IntegrationTests/OpenAIConfiguration.cs" />
@@ -336,6 +344,7 @@
336344
<Project Path="src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj" />
337345
<Project Path="src/Microsoft.Agents.AI.Abstractions/Microsoft.Agents.AI.Abstractions.csproj" />
338346
<Project Path="src/Microsoft.Agents.AI.AGUI/Microsoft.Agents.AI.AGUI.csproj" />
347+
<Project Path="src/Microsoft.Agents.AI.Anthropic/Microsoft.Agents.AI.Anthropic.csproj" />
339348
<Project Path="src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
340349
<Project Path="src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
341350
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
@@ -358,6 +367,7 @@
358367
<Folder Name="/Tests/" />
359368
<Folder Name="/Tests/IntegrationTests/">
360369
<Project Path="tests/AgentConformance.IntegrationTests/AgentConformance.IntegrationTests.csproj" />
370+
<Project Path="tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj" />
361371
<Project Path="tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj" />
362372
<Project Path="tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj" />
363373
<Project Path="tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj" />
@@ -374,6 +384,7 @@
374384
<Project Path="tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj" />
375385
<Project Path="tests/Microsoft.Agents.AI.Abstractions.UnitTests/Microsoft.Agents.AI.Abstractions.UnitTests.csproj" />
376386
<Project Path="tests/Microsoft.Agents.AI.AGUI.UnitTests/Microsoft.Agents.AI.AGUI.UnitTests.csproj" />
387+
<Project Path="tests/Microsoft.Agents.AI.Anthropic.UnitTests/Microsoft.Agents.AI.Anthropic.UnitTests.csproj" />
377388
<Project Path="tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests.csproj" />
378389
<Project Path="tests/Microsoft.Agents.AI.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
379390
<Project Path="tests/Microsoft.Agents.AI.DevUI.UnitTests/Microsoft.Agents.AI.DevUI.UnitTests.csproj" />
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net10.0</TargetFrameworks>
6+
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<NoWarn>$(NoWarn);IDE0059</NoWarn>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Azure.Identity" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
18+
</ItemGroup>
19+
20+
</Project>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
// This sample shows how to create and use an AI agent with Anthropic as the backend.
4+
5+
using System.ClientModel;
6+
using System.Net.Http.Headers;
7+
using Anthropic;
8+
using Anthropic.Core;
9+
using Azure.Core;
10+
using Azure.Identity;
11+
using Microsoft.Agents.AI;
12+
using Sample;
13+
14+
var deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NAME") ?? "claude-haiku-4-5";
15+
16+
// The resource is the subdomain name / first name coming before '.services.ai.azure.com' in the endpoint Uri
17+
// ie: https://(resource name).services.ai.azure.com/anthropic/v1/chat/completions
18+
var resource = Environment.GetEnvironmentVariable("ANTHROPIC_RESOURCE");
19+
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");
20+
21+
const string JokerInstructions = "You are good at telling jokes.";
22+
const string JokerName = "JokerAgent";
23+
24+
AnthropicClient? client = (resource is null)
25+
? new AnthropicClient() { APIKey = apiKey ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is required when no ANTHROPIC_RESOURCE is provided") } // If no resource is provided, use Anthropic public API
26+
: (apiKey is not null)
27+
? new AnthropicFoundryClient(resource, new ApiKeyCredential(apiKey)) // If an apiKey is provided, use Foundry with ApiKey authentication
28+
: new AnthropicFoundryClient(resource, new AzureCliCredential()); // Otherwise, use Foundry with Azure Client authentication
29+
30+
AIAgent agent = client.CreateAIAgent(model: deploymentName, instructions: JokerInstructions, name: JokerName);
31+
32+
// Invoke the agent and output the text result.
33+
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
34+
35+
namespace Sample
36+
{
37+
/// <summary>
38+
/// Provides methods for invoking the Azure hosted Anthropic api.
39+
/// </summary>
40+
public class AnthropicFoundryClient : AnthropicClient
41+
{
42+
private readonly TokenCredential _tokenCredential;
43+
private readonly string _resourceName;
44+
45+
/// <summary>
46+
/// Creates a new instance of the <see cref="AnthropicFoundryClient"/>.
47+
/// </summary>
48+
/// <param name="resourceName">The service resource subdomain name to use in the anthropic azure endpoint</param>
49+
/// <param name="tokenCredential">The credential provider. Use any specialization of <see cref="TokenCredential"/> to get your access token in supported environments.</param>
50+
/// <param name="options">Set of <see cref="Anthropic.Core.ClientOptions"/> client option configurations</param>
51+
/// <exception cref="ArgumentNullException">Resource is null</exception>
52+
/// <exception cref="ArgumentNullException">TokenCredential is null</exception>
53+
/// <remarks>
54+
/// Any <see cref="Anthropic.Core.ClientOptions"/> APIKey or Bearer token provided will be ignored in favor of the <see cref="TokenCredential"/> provided in the constructor
55+
/// </remarks>
56+
public AnthropicFoundryClient(string resourceName, TokenCredential tokenCredential, Anthropic.Core.ClientOptions? options = null) : base(options ?? new())
57+
{
58+
this._resourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName));
59+
this._tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential));
60+
this.BaseUrl = new Uri($"https://{this._resourceName}.services.ai.azure.com/anthropic", UriKind.Absolute);
61+
}
62+
63+
/// <summary>
64+
/// Creates a new instance of the <see cref="AnthropicFoundryClient"/>.
65+
/// </summary>
66+
/// <param name="resourceName">The service resource subdomain name to use in the anthropic azure endpoint</param>
67+
/// <param name="apiKeyCredential">The api key.</param>
68+
/// <param name="options">Set of <see cref="Anthropic.Core.ClientOptions"/> client option configurations</param>
69+
/// <exception cref="ArgumentNullException">Resource is null</exception>
70+
/// <exception cref="ArgumentNullException">Api key is null</exception>
71+
/// <remarks>
72+
/// Any <see cref="Anthropic.Core.ClientOptions"/> APIKey or Bearer token provided will be ignored in favor of the <see cref="ApiKeyCredential"/> provided in the constructor
73+
/// </remarks>
74+
public AnthropicFoundryClient(string resourceName, ApiKeyCredential apiKeyCredential, Anthropic.Core.ClientOptions? options = null) :
75+
this(resourceName, apiKeyCredential is null
76+
? throw new ArgumentNullException(nameof(apiKeyCredential))
77+
: DelegatedTokenCredential.Create((_, _) =>
78+
{
79+
apiKeyCredential.Deconstruct(out string dangerousCredential);
80+
return new AccessToken(dangerousCredential, DateTimeOffset.MaxValue);
81+
}),
82+
options)
83+
{ }
84+
85+
public override IAnthropicClient WithOptions(Func<Anthropic.Core.ClientOptions, Anthropic.Core.ClientOptions> modifier)
86+
=> this;
87+
88+
protected override ValueTask BeforeSend<T>(
89+
HttpRequest<T> request,
90+
HttpRequestMessage requestMessage,
91+
CancellationToken cancellationToken
92+
)
93+
{
94+
var accessToken = this._tokenCredential.GetToken(new TokenRequestContext(scopes: ["https://ai.azure.com/.default"]), cancellationToken);
95+
96+
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken.Token);
97+
98+
return default;
99+
}
100+
}
101+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Creating an AIAgent with Anthropic
2+
3+
This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service.
4+
5+
The sample supports three deployment scenarios:
6+
7+
1. **Anthropic Public API** - Direct connection to Anthropic's public API
8+
2. **Azure Foundry with API Key** - Anthropic models deployed through Azure Foundry using API key authentication
9+
3. **Azure Foundry with Azure CLI** - Anthropic models deployed through Azure Foundry using Azure CLI credentials
10+
11+
## Prerequisites
12+
13+
Before you begin, ensure you have the following prerequisites:
14+
15+
- .NET 8.0 SDK or later
16+
17+
### For Anthropic Public API
18+
19+
- Anthropic API key
20+
21+
Set the following environment variables:
22+
23+
```powershell
24+
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
25+
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
26+
```
27+
28+
### For Azure Foundry with API Key
29+
30+
- Azure Foundry service endpoint and deployment configured
31+
- Anthropic API key
32+
33+
Set the following environment variables:
34+
35+
```powershell
36+
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
37+
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
38+
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
39+
```
40+
41+
### For Azure Foundry with Azure CLI
42+
43+
- Azure Foundry service endpoint and deployment configured
44+
- Azure CLI installed and authenticated (for Azure credential authentication)
45+
46+
Set the following environment variables:
47+
48+
```powershell
49+
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
50+
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
51+
```
52+
53+
**Note**: When using Azure Foundry with Azure CLI, 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).

dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>net10.0</TargetFrameworks>
5+
<TargetFramework>net10.0</TargetFramework>
66

77
<Nullable>enable</Nullable>
88
<ImplicitUsings>enable</ImplicitUsings>

dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// This sample shows how to create and use a simple AI agent with OpenAI Chat Completion as the backend.
44

55
using Microsoft.Agents.AI;
6+
using Microsoft.Extensions.AI;
67
using OpenAI;
78

89
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");

dotnet/samples/GettingStarted/AgentProviders/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ See the README.md for each sample for the prerequisites for that sample.
1515
|Sample|Description|
1616
|---|---|
1717
|[Creating an AIAgent with A2A](./Agent_With_A2A/)|This sample demonstrates how to create AIAgent for an existing A2A agent.|
18+
|[Creating an AIAgent with Anthropic](./Agent_With_Anthropic/)|This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service|
1819
|[Creating an AIAgent with Foundry Agents using Azure.AI.Agents.Persistent](./Agent_With_AzureAIAgentsPersistent/)|This sample demonstrates how to create a Foundry Persistent agent and expose it as an AIAgent using the Azure.AI.Agents.Persistent SDK|
1920
|[Creating an AIAgent with Foundry Agents using Azure.AI.Project](./Agent_With_AzureAIProject/)|This sample demonstrates how to create an Foundry Project agent and expose it as an AIAgent using the Azure.AI.Project SDK|
2021
|[Creating an AIAgent with AzureFoundry Model](./Agent_With_AzureFoundryModel/)|This sample demonstrates how to use any model deployed to Azure Foundry to create an AIAgent|
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net10.0</TargetFramework>
6+
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
// This sample shows how to create and use a simple AI agent with Anthropic as the backend.
4+
5+
using Anthropic;
6+
using Anthropic.Core;
7+
using Microsoft.Agents.AI;
8+
using Microsoft.Extensions.AI;
9+
10+
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
11+
var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5";
12+
13+
AIAgent agent = new AnthropicClient(new ClientOptions { APIKey = apiKey })
14+
.CreateAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
15+
16+
// Invoke the agent and output the text result.
17+
var response = await agent.RunAsync("Tell me a joke about a pirate.");
18+
Console.WriteLine(response);
19+
20+
// Invoke the agent with streaming support.
21+
await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
22+
{
23+
Console.WriteLine(update);
24+
}

0 commit comments

Comments
 (0)