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
90 changes: 90 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Contributing to Anthropic.Net

Thank you for your interest in contributing to Anthropic.Net! We welcome contributions from the community to help improve this SDK.

## Development Environment

To get started, ensure you have the following installed:

- **.NET SDK**: Version 8.0 or later (see `global.json`).
- **IDE**: Visual Studio 2022, VS Code, or JetBrains Rider.
- **PowerShell**: Required for running build scripts.

## Getting Started

1. **Fork** the repository on GitHub.
2. **Clone** your fork locally:

```bash
git clone https://github.com/YOUR-USERNAME/anthropic.net.git
cd anthropic.net
```

3. **Restore** dependencies and tools:

```bash
dotnet tool restore
dotnet restore
```

## Building and Testing

The project uses [Cake](https://cakebuild.net/) for build automation. You can run the build script using the dotnet tool:

```bash
dotnet cake --target=Build
```

To run tests:

```bash
dotnet cake --target=Test
```

To run the full CI pipeline (Build, Test, Pack):

```bash
dotnet cake --target=Default
```

## Code Style

We enforce code style using `.editorconfig` and StyleCop analyzers.

- **Naming**: PascalCase for public members, camelCase for private fields/parameters.
- **Formatting**: Standard C# conventions (K&R braces, 4-space indentation).
- **Headers**: File headers are not required.
- **Warnings**: Treat warnings as errors is enabled in CI. Ensure your code builds without warnings.

Run `dotnet format` to automatically fix style issues.

## Pull Request Process

1. Create a new branch for your feature or fix:

```bash
git checkout -b feature/my-awesome-feature
```

2. Commit your changes with clear, descriptive messages.
3. Push your branch to your fork.
4. Open a Pull Request (PR) against the `main` branch of the `tinonetic/anthropic.net` repository.
5. Ensure all CI checks pass.

## Release Process (Maintainers Only)

Releases are automated via GitHub Actions and MinVer.

1. **Versioning**: Determine the next version number (SemVer).
2. **Tagging**: Create and push a git tag for the new version (e.g., `v2.0.0`).

```bash
git tag v2.0.0
git push origin v2.0.0
```

3. **Publishing**: Go to GitHub Releases, draft a new release for the tag, and click "Publish". This triggers the NuGet publication workflow.

## License

By contributing, you agree that your contributions will be licensed under the project's [MIT License](LICENSE.md).
2 changes: 1 addition & 1 deletion Source/Anthropic.Net/IAnthropicApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Anthropic.Net;
/// <summary>
/// The Anthropic API client interface.
/// </summary>
internal interface IAnthropicApiClient
public interface IAnthropicApiClient
{
/// <summary>
/// Sends a prompt to the Anthropic API for completion.
Expand Down
51 changes: 51 additions & 0 deletions Tests/Anthropic.Net.Tests/ApiClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace Anthropic.Net.Test;
using System.Net;
using Anthropic.Net.Constants;
using Anthropic.Net.Models.Messages;
using Anthropic.Net.Models.Messages.Streaming;
using Anthropic.Net.Models.Messages.Streaming.StreamingEvents;
using NSubstitute;
using Shouldly;
using Xunit;
Expand Down Expand Up @@ -82,4 +84,53 @@ public async Task TestMessageAsync_ValidRequestAsync()
response.Content[0].Type.ShouldBe("text");
((TextContentBlock)response.Content[0]).Text.ShouldBe("Hello, world!");
}
[Fact]
public async Task TestStreamMessageAsync_ValidRequestAsync()
{
// Arrange
var mockSseResponse = """
event: message_start
data: {"type": "message_start", "message": {"id": "msg_1", "type": "message", "role": "assistant", "content": [], "model": "claude-3-sonnet-20240229", "stop_reason": null, "stop_sequence": null, "usage": {"input_tokens": 25, "output_tokens": 1}}}

event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Hello"}}

event: content_block_stop
data: {"type": "content_block_stop", "index": 0}

event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": null}, "usage": {"output_tokens": 15}}

event: message_stop
data: {"type": "message_stop"}
""";

var messageHandler = new MockHttpMessageHandler(mockSseResponse, HttpStatusCode.OK);
var httpClientFactory = Substitute.For<IHttpClientFactory>();
_ = httpClientFactory.CreateClient().Returns(
new HttpClient(messageHandler)
{
BaseAddress = _baseAddress,
});

var sut = new AnthropicApiClient("test-api-key", httpClientFactory);

// Act
var messages = new List<Message> { Message.FromUser("Hello") };
var events = new List<MessageStreamEvent>();
await foreach (var ev in sut.StreamMessageAsync(new MessageRequest(AnthropicModels.Claude3Sonnet, messages)))
{
events.Add(ev);
}

// Assert
events.ShouldNotBeEmpty();
events.Count.ShouldBe(6);
events[0].ShouldBeOfType<MessageStartEvent>();
events[2].ShouldBeOfType<ContentBlockDeltaEvent>();
((ContentBlockDeltaEvent)events[2]).Delta.Text.ShouldBe("Hello");
}
}
Loading