fix: move client to top level namespace
This commit is contained in:
@@ -4,8 +4,9 @@ using System.Text.Json;
|
|||||||
|
|
||||||
using AnthropicClient.Json;
|
using AnthropicClient.Json;
|
||||||
using AnthropicClient.Utils;
|
using AnthropicClient.Utils;
|
||||||
|
using AnthropicClient.Models;
|
||||||
|
|
||||||
namespace AnthropicClient.Models;
|
namespace AnthropicClient;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a client for interacting with the Anthropic API.
|
/// Represents a client for interacting with the Anthropic API.
|
||||||
@@ -18,6 +19,13 @@ public interface IClient
|
|||||||
/// <param name="request">The chat message request to create.</param>
|
/// <param name="request">The chat message request to create.</param>
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the chat response as an <see cref="AnthropicResult{T}"/>.</returns>
|
/// <returns>A task that represents the asynchronous operation. The task result contains the chat response as an <see cref="AnthropicResult{T}"/>.</returns>
|
||||||
Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request);
|
Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a chat message asynchronously and streams the response.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The chat message request to create.</param>
|
||||||
|
/// <returns>An asynchronous enumerable that yields the chat response line by line.</returns>
|
||||||
|
IAsyncEnumerable<AnthropicEvent> CreateChatMessageAsync(StreamChatMessageRequest request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IClient"/>
|
/// <inheritdoc cref="IClient"/>
|
||||||
@@ -62,8 +70,7 @@ public class Client : IClient
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request)
|
public async Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request)
|
||||||
{
|
{
|
||||||
var requestContent = new StringContent(Serialize(request), Encoding.UTF8, JsonContentType);
|
var response = await SendRequestAsync(request);
|
||||||
var response = await _httpClient.PostAsync(MessagesEndpoint, requestContent);
|
|
||||||
var requestId = GetRequestId(response);
|
var requestId = GetRequestId(response);
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
@@ -77,6 +84,70 @@ public class Client : IClient
|
|||||||
return AnthropicResult<ChatResponse>.Success(chatResponse, requestId);
|
return AnthropicResult<ChatResponse>.Success(chatResponse, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async IAsyncEnumerable<AnthropicEvent> CreateChatMessageAsync(StreamChatMessageRequest request)
|
||||||
|
{
|
||||||
|
var response = await SendRequestAsync(request);
|
||||||
|
var requestId = GetRequestId(response);
|
||||||
|
|
||||||
|
using var responseContent = await response.Content.ReadAsStreamAsync();
|
||||||
|
using var streamReader = new StreamReader(responseContent);
|
||||||
|
|
||||||
|
var currentEvent = new AnthropicEvent();
|
||||||
|
|
||||||
|
// TODO: I'd like to emit custom events unique to this client
|
||||||
|
// - "content_block_complete"
|
||||||
|
// - provides the complete content block that was streamed
|
||||||
|
// - useful for streaming in text, but then handling tool calls with their entire input
|
||||||
|
|
||||||
|
// will need to keep track of current content so that we can build it up as relevant
|
||||||
|
// deltas are streamed in
|
||||||
|
|
||||||
|
// will want to capture the content block start event
|
||||||
|
// then continue to build this up until we get to the content block stop event
|
||||||
|
// at that point we should have the complete block and we should be able to yield
|
||||||
|
// return the complete block and reset the current content block
|
||||||
|
|
||||||
|
// event: content_block_start
|
||||||
|
// data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01T1x1fJ34qAmk2tNTrN7Up6","name":"get_weather","input":{}}}
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var line = await streamReader.ReadLineAsync();
|
||||||
|
|
||||||
|
if (line is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("event:"))
|
||||||
|
{
|
||||||
|
var eventType = line.Substring("event:".Length).Trim();
|
||||||
|
currentEvent = currentEvent with { Type = eventType };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("data:"))
|
||||||
|
{
|
||||||
|
var eventData = line.Substring("data:".Length).Trim();
|
||||||
|
var eventDataJson = JsonSerializer.Deserialize<EventData>(eventData, JsonSerializationOptions.DefaultOptions);
|
||||||
|
currentEvent = currentEvent with { Data = eventDataJson! };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line == string.Empty)
|
||||||
|
{
|
||||||
|
yield return currentEvent;
|
||||||
|
currentEvent = new AnthropicEvent();
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<HttpResponseMessage> SendRequestAsync(MessageRequest request)
|
||||||
|
{
|
||||||
|
var requestContent = new StringContent(Serialize(request), Encoding.UTF8, JsonContentType);
|
||||||
|
return await _httpClient.PostAsync(MessagesEndpoint, requestContent);
|
||||||
|
}
|
||||||
|
|
||||||
private string Serialize<T>(T obj) => JsonSerializer.Serialize(obj, JsonSerializationOptions.DefaultOptions);
|
private string Serialize<T>(T obj) => JsonSerializer.Serialize(obj, JsonSerializationOptions.DefaultOptions);
|
||||||
private T? Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializationOptions.DefaultOptions);
|
private T? Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializationOptions.DefaultOptions);
|
||||||
private string GetRequestId(HttpResponseMessage response) => response.Headers.GetValues(RequestIdHeader).FirstOrDefault() ?? string.Empty;
|
private string GetRequestId(HttpResponseMessage response) => response.Headers.GetValues(RequestIdHeader).FirstOrDefault() ?? string.Empty;
|
||||||
Reference in New Issue
Block a user