Skip to content

Commit 3a2cb98

Browse files
committed
feat: Added IChatClient streaming support with text only.
1 parent 1708406 commit 3a2cb98

File tree

3 files changed

+88
-26
lines changed

3 files changed

+88
-26
lines changed

src/libs/Anthropic/Extensions/AnthropicClient.ChatClient.cs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -117,36 +117,38 @@ async Task<ChatResponse> IChatClient.GetResponseAsync(
117117
async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync(
118118
IList<ChatMessage> chatMessages, ChatOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken)
119119
{
120-
// TODO: Implement full streaming support. For now, it just yields the CompleteAsync result.
121-
122-
ChatResponse response = await ((IChatClient)this).GetResponseAsync(chatMessages, options, cancellationToken).ConfigureAwait(false);
120+
CreateMessageParams request = CreateRequest(chatMessages, options);
121+
122+
IAsyncEnumerable<MessageStreamEvent> enumerable =
123+
CreateMessageAsStreamAsync(request, anthropicVersion: "2023-06-01", cancellationToken);
123124

124-
for (int i = 0; i < response.Choices.Count; i++)
125+
await foreach (var response in enumerable.ConfigureAwait(false))
125126
{
126-
ChatMessage choice = response.Choices[i];
127-
128-
yield return new()
127+
var chatResponseUpdate = new ChatResponseUpdate();
128+
if (response.IsContentBlockDelta)
129129
{
130-
AdditionalProperties = choice.AdditionalProperties,
131-
AuthorName = choice.AuthorName,
132-
ChatThreadId = response.ChatThreadId,
133-
ChoiceIndex = i,
134-
ResponseId = response.ResponseId,
135-
Contents = choice.Contents,
136-
CreatedAt = response.CreatedAt,
137-
FinishReason = response.FinishReason,
138-
ModelId = response.ModelId,
139-
RawRepresentation = choice.RawRepresentation,
140-
Role = choice.Role,
141-
};
142-
}
130+
var delta = response.ContentBlockDelta!.Delta;
131+
if (delta.IsTextDelta)
132+
{
133+
chatResponseUpdate.Contents.Add(new TextContent(delta.TextDelta!.Text) { RawRepresentation = delta.TextDelta!.Text });
134+
}
135+
}
143136

144-
if (response.Usage is not null)
145-
{
146-
yield return new()
147-
{
148-
Contents = [new UsageContent(response.Usage)],
149-
};
137+
yield return chatResponseUpdate;
138+
// yield return new ChatResponseUpdate
139+
// {
140+
// //AdditionalProperties = response.AdditionalProperties,
141+
// //AuthorName = choice.AuthorName,
142+
// ChatThreadId = response.ChatThreadId,
143+
// ChoiceIndex = i,
144+
// ResponseId = response.ResponseId,
145+
// Contents = choice.Contents,
146+
// CreatedAt = response.CreatedAt,
147+
// FinishReason = response.FinishReason,
148+
// ModelId = response.ModelId,
149+
// RawRepresentation = choice.RawRepresentation,
150+
// Role = choice.Role,
151+
// };
150152
}
151153
}
152154

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Microsoft.Extensions.AI;
2+
3+
namespace Anthropic.IntegrationTests;
4+
5+
public partial class Tests
6+
{
7+
[TestMethod]
8+
public async Task ChatClient_FiveRandomWords()
9+
{
10+
using var client = GetAuthenticatedChatClient();
11+
12+
var response = await client.GetResponseAsync(
13+
chatMessages: [new ChatMessage(ChatRole.User, "Generate 5 random words.")],
14+
new ChatOptions
15+
{
16+
ModelId = ModelVariant2.Claude37SonnetLatest.ToValueString(),
17+
});
18+
19+
Console.WriteLine(response.ToString());
20+
}
21+
22+
[TestMethod]
23+
public async Task ChatClient_FiveRandomWords_Streaming()
24+
{
25+
using var client = GetAuthenticatedChatClient();
26+
27+
var enumerable = client.GetStreamingResponseAsync(
28+
chatMessages: [new ChatMessage(ChatRole.User, "Generate 5 random words.")],
29+
new ChatOptions
30+
{
31+
ModelId = ModelVariant2.Claude37SonnetLatest.ToValueString(),
32+
});
33+
34+
var deltas = new List<string>();
35+
await foreach (var response in enumerable)
36+
{
37+
Console.Write(response.ToString());
38+
39+
deltas.Add(response.ToString());
40+
}
41+
42+
deltas.Should().NotBeEmpty().And.HaveCountGreaterThan(5);
43+
}
44+
}

src/tests/Anthropic.IntegrationTests/Tests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Microsoft.Extensions.AI;
2+
13
namespace Anthropic.IntegrationTests;
24

35
[TestClass]
@@ -16,4 +18,18 @@ private static AnthropicClient GetAuthenticatedClient()
1618

1719
return new AnthropicClient(apiKey);
1820
}
21+
22+
private static IChatClient GetAuthenticatedChatClient()
23+
{
24+
var apiKey =
25+
Environment.GetEnvironmentVariable("API_KEY") ??
26+
Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ??
27+
throw new AssertInconclusiveException("ANTHROPIC_API_KEY environment variable is not found.");
28+
if (string.IsNullOrWhiteSpace(apiKey))
29+
{
30+
throw new AssertInconclusiveException("ANTHROPIC_API_KEY environment variable is empty.");
31+
}
32+
33+
return new AnthropicClient(apiKey);
34+
}
1935
}

0 commit comments

Comments
 (0)