Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ToChatCompletion{Async} methods for combining StreamingChatCompletionUpdates #5605

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

stephentoub
Copy link
Member

@stephentoub stephentoub commented Nov 6, 2024

WithMessageAddedAsync enables writing code like:

List<ChatMessage> messages = ...;
await foreach (var update in client.CompleteStreamingAsync(messages).WithMessageAddedAsync(messages))
{
    Console.Write(update);
}

and upon completion of the loop, messages will contain a ChatMessage merged from all of the updates.

ToChatCompletion is what's used to achieve that, but is also exposed on its own so that code which has an enumerable of StreamingChatCompletionUpdates can merge them into a ChatCompletion. This enables a non-streaming implementation to easily be authored in terms of a streaming one.

Microsoft Reviewers: Open in CodeFlow

@stephentoub stephentoub requested a review from a team as a code owner November 6, 2024 22:22
@stephentoub stephentoub changed the title Add StreamingChatCompletionUpdateExtensions WithMessageAddedAsync / MergeUpdates helpers Add StreamingChatCompletionUpdateExtensions WithMessageAddedAsync / ToChatCompletion helpers Nov 7, 2024
@eiriktsarpalis
Copy link
Member

WithMessageAddedAsync enables writing code like:

I hadn't realized that CompleteStreamingAsync doesn't update the chat history after the IAE has been enumerated. I'm guessing that this is influenced by the deferred execution of the resultant IAE, but it does seem possible we could go the other way and require coalescence to be a part of CompleteStreamingAsync for consistency with CompleteAsync.

@stephentoub
Copy link
Member Author

WithMessageAddedAsync enables writing code like:

I hadn't realized that CompleteStreamingAsync doesn't update the chat history after the IAE has been enumerated. I'm guessing that this is influenced by the deferred execution of the resultant IAE, but it does seem possible we could go the other way and require coalescence to be a part of CompleteStreamingAsync for consistency with CompleteAsync.

Neither of them update the chat history with the resulting message.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Nov 7, 2024

The naming of WithMessageAddedAsync feels odd. Phrasing it in the past tense sounds as if the message has already been added in some way, but it adds the message in the future after the streaming completes.

Do any of the following feel right to you?

await foreach (var update in client.CompleteStreamingAsync(messages).AppendMessageTo(messages))
await foreach (var update in client.CompleteStreamingAsync(messages).AppendResultTo(messages))
await foreach (var update in client.CompleteStreamingAsync(messages).AppendResponseTo(messages))
await foreach (var update in client.CompleteStreamingAsync(messages).WithAppendTo(messages))
await foreach (var update in client.CompleteStreamingAsync(messages).ThenAppendTo(messages))

I mildly lean towards AppendResultTo or AppendResponseTo but don't feel strongly.

@eiriktsarpalis
Copy link
Member

I tend to associate the Append prefix with methods that return void; With more accurately reflects we're decorating the underlying IAE with behaviour; Then is typically used when appending callbacks in the style of Task.ContinueWith which isn't precisely what this method is doing.

@stephentoub
Copy link
Member Author

stephentoub commented Nov 7, 2024

The hard part here is the ToChatCompletion. With that, someone can do the equivalent of WithMessageAddedAsync with:

List<StreamingChatCompletionUpdate> updates = [];
await foreach (var update in client.CompleteStreamingAsync(messages))
{
    Console.Write(update);
    updates.Add(update);
}
messages.Add(updates.ToChatCompletion().Message);

Given that all of the raised concerns are about WithMessageAddedAsync, I'll just remove that for now.

@stephentoub
Copy link
Member Author

stephentoub commented Nov 7, 2024

This is now just:

public static ChatCompletion ToChatCompletion(
    this IEnumerable<StreamingChatCompletionUpdate> updates, bool coalesceContent = true)

@stephentoub
Copy link
Member Author

(though now that I write that out, I'm wondering if I should add one for IAsyncEnumerable as well)

@stephentoub stephentoub changed the title Add StreamingChatCompletionUpdateExtensions WithMessageAddedAsync / ToChatCompletion helpers Add ToChatCompletion{Async} methods for combining StreamingChatCompletionUpdates Nov 7, 2024
@stephentoub
Copy link
Member Author

Refactored to have ToChatCompletion{Async} and to have them in the Abstractions library. Having them there means a leaf client can choose to use e.g. ToChatCompletionAsync to implement its CompleteAsync method around its CompleteStreamingAsync, if desired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants