.Net: Update Anthropic and Anthropic.Foundry package versions (#3517)

* Initial plan

* Update Anthropic packages to v12.3.0 and Anthropic.Foundry to v0.4.1

Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com>

* Fix AnthropicClient not being disposed in sample

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com>
This commit is contained in:
Copilot
2026-01-30 08:48:19 -08:00
committed by GitHub
Unverified
parent cd7661bbb7
commit 924211a518
9 changed files with 28 additions and 69 deletions
+3 -3
View File
@@ -11,8 +11,8 @@
</PropertyGroup>
<ItemGroup>
<!-- Aspire.* -->
<PackageVersion Include="Anthropic" Version="12.0.1" />
<PackageVersion Include="Anthropic.Foundry" Version="0.1.0" />
<PackageVersion Include="Anthropic" Version="12.3.0" />
<PackageVersion Include="Anthropic.Foundry" Version="0.4.1" />
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="13.0.0-preview.1.25560.3" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireAppHostSdkVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="$(AspireAppHostSdkVersion)" />
@@ -42,7 +42,7 @@
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.2" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="10.0.0" />
<PackageVersion Include="System.Net.Http.Json" Version="10.0.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.1" />
<PackageVersion Include="System.Text.Json" Version="10.0.2" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.2" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
@@ -2,15 +2,12 @@
// This sample shows how to create and use an AI agent with Anthropic as the backend.
using System.Net.Http.Headers;
using Anthropic;
using Anthropic.Foundry;
using Azure.Core;
using Azure.Identity;
using Microsoft.Agents.AI;
using Sample;
var deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NAME") ?? "claude-haiku-4-5";
string deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NAME") ?? "claude-haiku-4-5";
// The resource is the subdomain name / first name coming before '.services.ai.azure.com' in the endpoint Uri
// ie: https://(resource name).services.ai.azure.com/anthropic/v1/chat/completions
@@ -20,55 +17,13 @@ string? apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");
const string JokerInstructions = "You are good at telling jokes.";
const string JokerName = "JokerAgent";
AnthropicClient? client = (resource is null)
? 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
using AnthropicClient client = (resource is null)
? 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
: (apiKey is not null)
? new AnthropicFoundryClient(new AnthropicFoundryApiKeyCredentials(apiKey, resource)) // If an apiKey is provided, use Foundry with ApiKey authentication
: new AnthropicFoundryClient(new AnthropicAzureTokenCredential(new AzureCliCredential(), resource)); // Otherwise, use Foundry with Azure Client authentication
: new AnthropicFoundryClient(new AnthropicFoundryIdentityTokenCredentials(new AzureCliCredential(), resource, ["https://ai.azure.com/.default"])); // Otherwise, use Foundry with Azure TokenCredential authentication
AIAgent agent = client.AsAIAgent(model: deploymentName, instructions: JokerInstructions, name: JokerName);
// Invoke the agent and output the text result.
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
namespace Sample
{
/// <summary>
/// Provides methods for invoking the Azure hosted Anthropic models using <see cref="TokenCredential"/> types.
/// </summary>
public sealed class AnthropicAzureTokenCredential : IAnthropicFoundryCredentials
{
private readonly TokenCredential _tokenCredential;
private readonly Lock _lock = new();
private AccessToken? _cachedAccessToken;
/// <inheritdoc/>
public string ResourceName { get; }
/// <summary>
/// Creates a new instance of the <see cref="AnthropicAzureTokenCredential"/>.
/// </summary>
/// <param name="tokenCredential">The credential provider. Use any specialization of <see cref="TokenCredential"/> to get your access token in supported environments.</param>
/// <param name="resourceName">The service resource subdomain name to use in the anthropic azure endpoint</param>
internal AnthropicAzureTokenCredential(TokenCredential tokenCredential, string resourceName)
{
this.ResourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName));
this._tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential));
}
/// <inheritdoc/>
public void Apply(HttpRequestMessage requestMessage)
{
lock (this._lock)
{
// Add a 5-minute buffer to avoid using tokens that are about to expire
if (this._cachedAccessToken is null || this._cachedAccessToken.Value.ExpiresOn <= DateTimeOffset.Now.AddMinutes(5))
{
this._cachedAccessToken = this._tokenCredential.GetToken(new TokenRequestContext(scopes: ["https://ai.azure.com/.default"]), CancellationToken.None);
}
}
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", this._cachedAccessToken.Value.Token);
}
}
}
@@ -10,7 +10,7 @@ using Microsoft.Extensions.AI;
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5";
AIAgent agent = new AnthropicClient(new ClientOptions { APIKey = apiKey })
AIAgent agent = new AnthropicClient(new ClientOptions { ApiKey = apiKey })
.AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
// Invoke the agent and output the text result.
@@ -13,7 +13,7 @@ var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-hai
var maxTokens = 4096;
var thinkingTokens = 2048;
var agent = new AnthropicClient(new ClientOptions { APIKey = apiKey })
var agent = new AnthropicClient(new ClientOptions { ApiKey = apiKey })
.AsAIAgent(
model: model,
clientFactory: (chatClient) => chatClient
@@ -22,7 +22,7 @@ const string AssistantName = "WeatherAssistant";
AITool tool = AIFunctionFactory.Create(GetWeather);
// Get anthropic client to create agents.
AIAgent agent = new AnthropicClient { APIKey = apiKey }
AIAgent agent = new AnthropicClient { ApiKey = apiKey }
.AsAIAgent(model: model, instructions: AssistantInstructions, name: AssistantName, tools: [tool]);
// Non-streaming agent interaction with function tools.
@@ -16,7 +16,7 @@ IChatClient aws = new AmazonBedrockRuntimeClient(
.AsIChatClient("amazon.nova-pro-v1:0");
IChatClient anthropic = new Anthropic.AnthropicClient(
new() { APIKey = Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY") })
new() { ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY") })
.AsIChatClient("claude-sonnet-4-20250514");
IChatClient openai = new OpenAI.OpenAIClient(
@@ -52,7 +52,7 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture
string instructions = "You are a helpful assistant.",
IList<AITool>? aiTools = null)
{
var anthropicClient = new AnthropicClient() { APIKey = s_config.ApiKey };
var anthropicClient = new AnthropicClient() { ApiKey = s_config.ApiKey };
IChatClient? chatClient = this._useBeta
? anthropicClient
@@ -242,7 +242,7 @@ public sealed class AnthropicBetaServiceExtensionsTests
var client = new AnthropicClient
{
HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") },
APIKey = "test-key"
ApiKey = "test-key"
};
// Act
@@ -436,13 +436,15 @@ public sealed class AnthropicBetaServiceExtensionsTests
}
public HttpClient HttpClient { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public Uri BaseUrl { get => new("http://localhost"); init => throw new NotImplementedException(); }
public string BaseUrl { get => "http://localhost"; init => throw new NotImplementedException(); }
public bool ResponseValidation { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public int? MaxRetries { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public TimeSpan? Timeout { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? APIKey { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? ApiKey { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? AuthToken { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public IAnthropicClientWithRawResponse WithRawResponse => throw new NotImplementedException();
public IMessageService Messages => throw new NotImplementedException();
public IModelService Models => throw new NotImplementedException();
@@ -453,14 +455,13 @@ public sealed class AnthropicBetaServiceExtensionsTests
IMessageService IAnthropicClient.Messages => new Mock<IMessageService>().Object;
public Task<HttpResponse> Execute<T>(HttpRequest<T> request, CancellationToken cancellationToken = default) where T : ParamsBase
public IAnthropicClient WithOptions(Func<ClientOptions, ClientOptions> modifier)
{
throw new NotImplementedException();
}
public IAnthropicClient WithOptions(Func<ClientOptions, ClientOptions> modifier)
public void Dispose()
{
throw new NotImplementedException();
}
private sealed class TestBetaService : IBetaService
@@ -472,6 +473,8 @@ public sealed class AnthropicBetaServiceExtensionsTests
this._client = client;
}
public IBetaServiceWithRawResponse WithRawResponse => throw new NotImplementedException();
public global::Anthropic.Services.Beta.IModelService Models => throw new NotImplementedException();
public global::Anthropic.Services.Beta.IFileService Files => throw new NotImplementedException();
@@ -66,27 +66,28 @@ public sealed class AnthropicClientExtensionsTests
}
public HttpClient HttpClient { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public Uri BaseUrl { get => new("http://localhost"); init => throw new NotImplementedException(); }
public string BaseUrl { get => "http://localhost"; init => throw new NotImplementedException(); }
public bool ResponseValidation { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public int? MaxRetries { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public TimeSpan? Timeout { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? APIKey { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? ApiKey { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public string? AuthToken { get => throw new NotImplementedException(); init => throw new NotImplementedException(); }
public IAnthropicClientWithRawResponse WithRawResponse => throw new NotImplementedException();
public IMessageService Messages => throw new NotImplementedException();
public IModelService Models => throw new NotImplementedException();
public IBetaService Beta => throw new NotImplementedException();
public Task<HttpResponse> Execute<T>(HttpRequest<T> request, CancellationToken cancellationToken = default) where T : ParamsBase
public IAnthropicClient WithOptions(Func<ClientOptions, ClientOptions> modifier)
{
throw new NotImplementedException();
}
public IAnthropicClient WithOptions(Func<ClientOptions, ClientOptions> modifier)
public void Dispose()
{
throw new NotImplementedException();
}
}
@@ -309,7 +310,7 @@ public sealed class AnthropicClientExtensionsTests
var client = new AnthropicClient
{
HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") },
APIKey = "test-key"
ApiKey = "test-key"
};
// Act