Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -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)" />
Expand Down Expand Up @@ -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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,55 +17,13 @@
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static string GetWeather([Description("The location to get the weather for.")] s
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
.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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public Task<ChatClientAgent> CreateChatClientAgentAsync(
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync()
var client = new AnthropicClient
{
HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") },
APIKey = "test-key"
ApiKey = "test-key"
};

// Act
Expand Down Expand Up @@ -436,13 +436,15 @@ public TestAnthropicChatClient()
}

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();
Expand All @@ -453,14 +455,13 @@ public TestAnthropicChatClient()

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
Expand All @@ -472,6 +473,8 @@ public TestBetaService(IAnthropicClient client)
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,28 @@ public TestAnthropicChatClient()
}

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();
}
}

Expand Down Expand Up @@ -309,7 +310,7 @@ public async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync()
var client = new AnthropicClient
{
HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") },
APIKey = "test-key"
ApiKey = "test-key"
};

// Act
Expand Down
Loading