mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.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:
committed by
GitHub
Unverified
parent
cd7661bbb7
commit
924211a518
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+9
-6
@@ -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();
|
||||
|
||||
+7
-6
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user