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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "10.1.2"
".": "10.2.0"
}
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 10.2.0 (2025-11-20)

Full Changelog: [v10.1.2...v10.2.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.1.2...v10.2.0)

### Features

* **client:** additional methods for positional params ([8bc6323](https://github.com/anthropics/anthropic-sdk-csharp/commit/8bc6323c38ce551f995bec5e4b1584460b7f037b))


### Bug Fixes

* **client:** return correct type for foundry#WithOptions ([#18](https://github.com/anthropics/anthropic-sdk-csharp/issues/18)) ([f814a46](https://github.com/anthropics/anthropic-sdk-csharp/commit/f814a460503abf7fdf7a824b5bf446ef74d60f28))
* use correct versions ([c78c8db](https://github.com/anthropics/anthropic-sdk-csharp/commit/c78c8db4b6effa6b1438bb879bcafdad2d155808))


### Refactors

* **client:** make unknown variants implicit ([eb0e5b6](https://github.com/anthropics/anthropic-sdk-csharp/commit/eb0e5b628d7090adc34300775043ecd26ccfffaf))

## 10.1.2 (2025-11-18)

Full Changelog: [v10.1.1...v10.1.2](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.1.1...v10.1.2)
Expand Down
6 changes: 2 additions & 4 deletions examples/MessagesExample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using Anthropic;
using Anthropic.Models.Messages;
using Anthropic.Foundry;

using Anthropic.Models.Messages;

// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
IAnthropicClient client = new AnthropicClient();
Expand All @@ -25,8 +24,7 @@
var message = String.Join(
"",
response
.Content
.Where(message => message.Value is TextBlock)
.Content.Where(message => message.Value is TextBlock)
.Select(message => message.Value as TextBlock)
.Select((textBlock) => textBlock.Text)
);
Expand Down
95 changes: 42 additions & 53 deletions src/Anthropic.Foundry/Anthropic.Foundry.csproj
Original file line number Diff line number Diff line change
@@ -1,58 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Anthropic.Foundry</RootNamespace>
<AssemblyName>Anthropic.Foundry</AssemblyName>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Anthropic\Anthropic.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure.Authentication"
Version="2.4.1" />
<PackageReference Include="Azure.Identity" Version="1.17.0" />
</ItemGroup>

<PropertyGroup>
<BuildPackage>true</BuildPackage>
<Deterministic>true</Deterministic>
<PackageId>Anthropic.Foundry</PackageId>
<Version>0.0.1</Version>
<Authors>Stainless Software, Inc.</Authors>
<Company>Stainless Software, Inc.</Company>
<PackageProjectUrl>https://github.com/anthropics/anthropic-sdk-csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/anthropics/anthropic-sdk-csharp</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>PRE-RELEASE NOT FOR DISTRIBUTION</PackageReleaseNotes>
<LangVersion>Latest</LangVersion>

<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository>
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Anthropic.Foundry</RootNamespace>
<AssemblyName>Anthropic.Foundry</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Anthropic\Anthropic.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure.Authentication" Version="2.4.1" />
<PackageReference Include="Azure.Identity" Version="1.17.0" />
</ItemGroup>
<PropertyGroup>
<BuildPackage>true</BuildPackage>
<Deterministic>true</Deterministic>
<PackageId>Anthropic.Foundry</PackageId>
<Version>0.0.2</Version>
<Authors>Stainless Software, Inc.</Authors>
<Company>Stainless Software, Inc.</Company>
<PackageProjectUrl>https://github.com/anthropics/anthropic-sdk-csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/anthropics/anthropic-sdk-csharp</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>PRE-RELEASE NOT FOR DISTRIBUTION</PackageReleaseNotes>
<LangVersion>Latest</LangVersion>
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository>
element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>

<!-- Optional: Embed source files that are not tracked by the source control manager in the
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager in the
PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>

<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<AllowedOutputExtensionsInPackageBuildOutputFolder>
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

<SupportedFrameworks>net8.0;net9.0;netstandard2.0</SupportedFrameworks>
</PropertyGroup>

<ItemGroup>
<None Include="../Anthropic/GlobalShims.cs">
<Link>%(RecursiveDir)/%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<SupportedFrameworks>net8.0;net9.0;netstandard2.0</SupportedFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="../Anthropic/GlobalShims.cs">
<Link>%(RecursiveDir)/%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public AnthropicFoundryBearerTokenCredentials(string apiKey, string resourceName

public void Apply(HttpRequestMessage requestMessage)
{
requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _apiKey);
requestMessage.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _apiKey);
}
}
36 changes: 31 additions & 5 deletions src/Anthropic.Foundry/AnthropicFoundryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
global using ValueTask = System.Threading.Tasks.Task;
#endif

using Anthropic.Core;
using Anthropic;
using Anthropic.Core;

namespace Anthropic.Foundry;

Expand All @@ -21,21 +21,47 @@ public class AnthropicFoundryClient : AnthropicClient
/// <exception cref="ArgumentNullException"></exception>
public AnthropicFoundryClient(IAnthropicFoundryCredentials azureCredentials)
{
_azureCredentials = azureCredentials ?? throw new ArgumentNullException(nameof(azureCredentials));
_azureCredentials =
azureCredentials ?? throw new ArgumentNullException(nameof(azureCredentials));
var url = $"https://{azureCredentials.ResourceName}.services.ai.azure.com/anthropic";
BaseUrl = new Uri(url, UriKind.Absolute);
}

private AnthropicFoundryClient(
IAnthropicFoundryCredentials azureCredentials,
ClientOptions options
)
: base(options)
{
_azureCredentials =
azureCredentials ?? throw new ArgumentNullException(nameof(azureCredentials));
}

[Obsolete("The {nameof(APIKey)} property is not supported in this configuration.", true)]
#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member
public override string? APIKey
#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member
{
get => throw new NotSupportedException($"The {nameof(APIKey)} property is not supported in this configuration.");
init => throw new NotSupportedException($"The {nameof(APIKey)} property is not supported in this configuration.");
get =>
throw new NotSupportedException(
$"The {nameof(APIKey)} property is not supported in this configuration."
);
init =>
throw new NotSupportedException(
$"The {nameof(APIKey)} property is not supported in this configuration."
);
}

public override IAnthropicClient WithOptions(Func<ClientOptions, ClientOptions> modifier)
{
return new AnthropicFoundryClient(_azureCredentials, modifier(_options));
}

protected override ValueTask BeforeSend<T>(HttpRequest<T> request, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
protected override ValueTask BeforeSend<T>(
HttpRequest<T> request,
HttpRequestMessage requestMessage,
CancellationToken cancellationToken
)
{
_azureCredentials.Apply(requestMessage);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ public AnthropicFoundryIdentityTokenCredentials(AccessToken apiKey, string resou
{
if (apiKey.Equals(default) || string.IsNullOrWhiteSpace(apiKey.Token))
{
throw new ArgumentNullException(nameof(apiKey), "Invalid/Empty api key struct is not valid");
throw new ArgumentNullException(
nameof(apiKey),
"Invalid/Empty api key struct is not valid"
);
}
_apiKey = apiKey;
ResourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName));
Expand All @@ -29,6 +32,7 @@ public AnthropicFoundryIdentityTokenCredentials(AccessToken apiKey, string resou
public void Apply(HttpRequestMessage requestMessage)
{
//TODO implement token refresh
requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _apiKey.Token);
requestMessage.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _apiKey.Token);
}
}
35 changes: 24 additions & 11 deletions src/Anthropic.Foundry/IAnthropicFoundryCredentials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,47 @@ public class DefaultAnthropicFoundryCredentials
/// ANTHROPIC_FOUNDRY_API_KEY=your_api_key
/// </code>
/// or use Azure Identity to provide a token.
///
///
/// If both are set, the API key environment variable will take precedence.
///
///
/// </remarks>
/// <param name="resourceName">The resource name or if null loaded from the environment variable <c>ANTHROPIC_FOUNDRY_RESOURCE</c></param>
/// <returns></returns>
public static async ValueTask<IAnthropicFoundryCredentials?> FromEnv(string? resourceName = null)
public static async ValueTask<IAnthropicFoundryCredentials?> FromEnv(
string? resourceName = null
)
{
if(Environment.GetEnvironmentVariable("ANTHROPIC_FOUNDRY_RESOURCE") is not string envResourceName
|| (string.IsNullOrWhiteSpace(resourceName) && string.IsNullOrWhiteSpace(envResourceName)))
if (
Environment.GetEnvironmentVariable("ANTHROPIC_FOUNDRY_RESOURCE")
is not string envResourceName
|| (
string.IsNullOrWhiteSpace(resourceName)
&& string.IsNullOrWhiteSpace(envResourceName)
)
)
{
return null;
}

if (Environment.GetEnvironmentVariable("ANTHROPIC_FOUNDRY_API_KEY") is string apiKey && !string.IsNullOrWhiteSpace(apiKey))
if (
Environment.GetEnvironmentVariable("ANTHROPIC_FOUNDRY_API_KEY") is string apiKey
&& !string.IsNullOrWhiteSpace(apiKey)
)
{
return new AnthropicFoundryApiKeyCredentials(apiKey, resourceName ?? envResourceName);
}

var defaultCredentialsProvider = new DefaultAzureCredential();

var azureToken = await defaultCredentialsProvider.GetTokenAsync(new()
{

}).ConfigureAwait(false);
var azureToken = await defaultCredentialsProvider
.GetTokenAsync(new() { })
.ConfigureAwait(false);
if (!azureToken.Equals(default))
{
return new AnthropicFoundryIdentityTokenCredentials(azureToken, resourceName ?? envResourceName);
return new AnthropicFoundryIdentityTokenCredentials(
azureToken,
resourceName ?? envResourceName
);
}

return null;
Expand Down
34 changes: 20 additions & 14 deletions src/Anthropic.Tests/AnthropicTestClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,43 @@ public AnthropicTestClientsAttribute(TestSupportTypes testSupportTypes = TestSup

public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var dataServiceUrl = Environment.GetEnvironmentVariable("TEST_API_BASE_URL") ?? "http://localhost:4010";
var dataServiceUrl =
Environment.GetEnvironmentVariable("TEST_API_BASE_URL") ?? "http://localhost:4010";
string apiKey = "YourApiKeyHere";
var resource = "YourRegionOrResourceHere";

var testData = testMethod.GetCustomAttributes<AnthropicTestDataAttribute>().ToArray();
if (TestSupportTypes.HasFlag(TestSupportTypes.Anthropic))
{
yield return [
new AnthropicClient()
{
BaseUrl = new Uri(dataServiceUrl),
APIKey = apiKey,
},
..testData.Where(e => e.TestSupport.HasFlag(TestSupportTypes.Anthropic)).Select(f => f.TestData).ToArray()
yield return
[
new AnthropicClient() { BaseUrl = new Uri(dataServiceUrl), APIKey = apiKey },
.. testData
.Where(e => e.TestSupport.HasFlag(TestSupportTypes.Anthropic))
.Select(f => f.TestData)
.ToArray(),
];
}
if (TestSupportTypes.HasFlag(TestSupportTypes.Foundry))
{
yield return [
new AnthropicFoundryClient(new AnthropicFoundryApiKeyCredentials(apiKey, resource!)) {
BaseUrl = new Uri(dataServiceUrl)
yield return
[
new AnthropicFoundryClient(new AnthropicFoundryApiKeyCredentials(apiKey, resource!))
{
BaseUrl = new Uri(dataServiceUrl),
},
..testData.Where(e => e.TestSupport.HasFlag(TestSupportTypes.Foundry)).Select(f => f.TestData).ToArray()
.. testData
.Where(e => e.TestSupport.HasFlag(TestSupportTypes.Foundry))
.Select(f => f.TestData)
.ToArray(),
];
}
}
}

[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
sealed class AnthropicTestDataAttribute : Attribute
{
{
public AnthropicTestDataAttribute(TestSupportTypes testSupport, object testData)
{
TestSupport = testSupport;
Expand All @@ -64,5 +70,5 @@ public enum TestSupportTypes
{
All = Anthropic | Foundry,
Anthropic = 1 << 1,
Foundry = 1 << 2
Foundry = 1 << 2,
}
Loading