Skip to content

Commit

Permalink
Rename the AsMessages extension method (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcominerva authored Mar 22, 2023
2 parents f0a94d4 + 60b1a92 commit 5e3bf62
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 24 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ The **AddChatGpt** method has an overload that accepts an [IServiceProvider](htt

The library can be used in any .NET application built with .NET 6.0 or later. For example, we can create a Minimal API in this way:

app.MapPost("/api/ask", async (Request request, IChatGptClient chatGptClient) =>
app.MapPost("/api/chat/ask", async (Request request, IChatGptClient chatGptClient) =>
{
var response = await chatGptClient.AskAsync(request.ConversationId, request.Message);
return TypedResults.Ok(response);
Expand All @@ -67,7 +67,7 @@ We can pass this value in subsequent invocations of **AskAsync** so that the lib

**Response streaming**

Chat completion API supports response streaming. When using this feature, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available. **ChatGptNet** provides response streaming using the **AskStreamAsync** method:
Chat completion API supports response streaming. When using this feature, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available. **ChatGptNet** provides response streaming using the **AskStreamAsync** method:

// Requests a streaming response.
var responseStream = chatGptClient.AskStreamAsync(conversationId, message);
Expand Down
6 changes: 3 additions & 3 deletions samples/ChatGptApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ async IAsyncEnumerable<string> Stream()
// Requests a streaming response.
var responseStream = chatGptClient.AskStreamAsync(conversationId.GetValueOrDefault(), message);

// Uses the "AsMessages" extension method to retrieve the partial message deltas only.
await foreach (var response in responseStream.AsMessages())
// Uses the "AsDeltas" extension method to retrieve partial message deltas only.
await foreach (var delta in responseStream.AsDeltas())
{
yield return response;
yield return delta;
await Task.Delay(50);
}
}
Expand Down
42 changes: 26 additions & 16 deletions src/ChatGptNet/ChatGptClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,25 @@ public async IAsyncEnumerable<ChatGptResponse> AskStreamAsync(Guid conversationI
var json = line["data: ".Length..];
var response = JsonSerializer.Deserialize<ChatGptResponse>(json, jsonSerializerOptions);

var content = response!.Choices[0].Delta!.Content ?? string.Empty;
var content = response!.Choices?[0].Delta?.Content;

if (contentBuilder.Length == 0)
if (!string.IsNullOrEmpty(content))
{
// If this is the first response, trims all the initial special characters.
content = contentBuilder.Length == 0 ? content.TrimStart('\n') : content;
response.Choices[0].Delta!.Content = content;
}

contentBuilder.Append(content);

// Yields the result only if text has been added to the content.
if (contentBuilder.Length > 0)
{
response.ConversationId = conversationId;
yield return response;
if (contentBuilder.Length == 0)
{
// If this is the first response, trims all the initial special characters.
content = content.TrimStart('\n');
response.Choices![0].Delta!.Content = content;
}

// Yields the response only if there is an actual content.
if (content != string.Empty)
{
contentBuilder.Append(content);

response.ConversationId = conversationId;
yield return response;
}
}
}
else if (line.StartsWith("data: [DONE]"))
Expand Down Expand Up @@ -209,10 +212,17 @@ public async IAsyncEnumerable<ChatGptResponse> AskStreamAsync(Guid conversationI

cache.Set(conversationId, messages, options.MessageExpiration);
}
else if (options.ThrowExceptionOnError)
else
{
var response = await httpResponse.Content.ReadFromJsonAsync<ChatGptResponse>(cancellationToken: cancellationToken);
throw new ChatGptException(response!.Error!, httpResponse.StatusCode);

if (options.ThrowExceptionOnError)
{
throw new ChatGptException(response!.Error!, httpResponse.StatusCode);
}

response!.ConversationId = conversationId;
yield return response;
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/ChatGptNet/Extensions/ChatGptResponseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ namespace ChatGptNet.Extensions;
public static class ChatGptResponseExtensions
{
/// <summary>
/// Returns an <see cref="IAsyncEnumerable{T}"/> that allows to enumerate all the response messages, each of them containing a partial delta.
/// Returns an <see cref="IAsyncEnumerable{T}"/> that allows to enumerate all the partial message deltas.
/// </summary>
/// <param name="responses">The source <see cref="IAsyncEnumerable{ChatGptResponse}"/>.</param>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> that allows to enumerate all the response messages, each of them containing a partial delta.</returns>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> that allows to enumerate all the partial message deltas.</returns>
/// <seealso cref="ChatGptResponse"/>
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "This method returns an IAsyncEnumerable")]
public static async IAsyncEnumerable<string> AsMessages(this IAsyncEnumerable<ChatGptResponse> responses)
public static async IAsyncEnumerable<string> AsDeltas(this IAsyncEnumerable<ChatGptResponse> responses)
{
await foreach (var response in responses)
{
Expand Down

0 comments on commit 5e3bf62

Please sign in to comment.