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

Azure OpenAI: Revise streaming behavior for better usability (completions + chat completions) #39347

Merged
merged 24 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d591c81
DRAFT: unvetted proposal for flattened streaming
trrwilson Oct 18, 2023
6dfd14a
add ported functions test
trrwilson Oct 18, 2023
4417b07
remaining tests ported
trrwilson Oct 18, 2023
ac477ee
completions for consistency
trrwilson Oct 18, 2023
9d2b330
comments, tests, and other cleanup
trrwilson Oct 19, 2023
2e3f43f
one orphaned test comment cleanup
trrwilson Oct 19, 2023
9133358
xml comment fix
trrwilson Oct 19, 2023
6b520ea
test assets, making it real
trrwilson Oct 19, 2023
5694c90
test assets pt. 2 (re-record functions)
trrwilson Oct 19, 2023
e1b24f6
revised pattern using new StreamingResponse<T>
trrwilson Oct 20, 2023
1a6a3a3
use delegate resolver for stronger response/enum connection
trrwilson Oct 20, 2023
42066ad
add a snippet for streaming functions
trrwilson Oct 23, 2023
a5312a9
also add a snippet for streaming with multiple choices
trrwilson Oct 23, 2023
046e026
speculative CHANGELOG update
trrwilson Oct 23, 2023
56710f6
basic, standalone test coverage for StreamingResponse<T>
trrwilson Oct 25, 2023
875b482
Merge branch 'main' into user/travisw/aoai-new-chat-streaming
trrwilson Oct 27, 2023
6df8f6e
feedback: keep StreamingResponse<T> in Azure.AI.OpenAI
trrwilson Oct 27, 2023
0411978
address missing 'using' on JsonDocument
trrwilson Oct 28, 2023
fc5cc1d
Merge branch 'main' into user/travisw/aoai-new-chat-streaming
trrwilson Oct 28, 2023
b8472cc
tie up broken link from changelog
trrwilson Oct 30, 2023
f06ead1
Merge branch 'main' into user/travisw/aoai-new-chat-streaming
trrwilson Oct 30, 2023
e8073ec
Merge branch 'main' into user/travisw/aoai-new-chat-streaming
trrwilson Oct 30, 2023
b9e7702
Merge branch 'main' into user/travisw/aoai-new-chat-streaming
trrwilson Nov 1, 2023
05b8a4c
Post-merge: export-api and update-snippets
trrwilson Nov 1, 2023
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
16 changes: 8 additions & 8 deletions sdk/openai/Azure.AI.OpenAI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,18 @@ var chatCompletionsOptions = new ChatCompletionsOptions()
}
};

Response<StreamingChatCompletions> response = await client.GetChatCompletionsStreamingAsync(
await foreach (StreamingChatCompletionsUpdate chatUpdate in client.GetChatCompletionsStreaming(
deploymentOrModelName: "gpt-3.5-turbo",
chatCompletionsOptions);
using StreamingChatCompletions streamingChatCompletions = response.Value;

await foreach (StreamingChatChoice choice in streamingChatCompletions.GetChoicesStreaming())
chatCompletionsOptions))
{
await foreach (ChatMessage message in choice.GetMessageStreaming())
if (chatUpdate.Role.HasValue)
{
Console.Write($"{chatUpdate.Role.Value.ToString().ToUpperInvariant()}: ");
}
if (!string.IsNullOrEmpty(chatUpdate.ContentUpdate))
{
Console.Write(message.Content);
Console.Write(chatUpdate.ContentUpdate);
}
Console.WriteLine();
}
```

Expand Down
69 changes: 29 additions & 40 deletions sdk/openai/Azure.AI.OpenAI/api/Azure.AI.OpenAI.netstandard2.0.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
namespace Azure
{
public partial class StreamingResponse<T> : System.Collections.Generic.IAsyncEnumerable<T>, System.IDisposable
trrwilson marked this conversation as resolved.
Show resolved Hide resolved
{
internal StreamingResponse() { }
public static Azure.StreamingResponse<T> CreateFromResponse(Azure.Response response, System.Func<Azure.Response, System.Collections.Generic.IAsyncEnumerable<T>> asyncEnumerableProcessor) { throw null; }
trrwilson marked this conversation as resolved.
Show resolved Hide resolved
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.Generic.IAsyncEnumerable<T> EnumerateValues() { throw null; }
public Azure.Response GetRawResponse() { throw null; }
System.Collections.Generic.IAsyncEnumerator<T> System.Collections.Generic.IAsyncEnumerable<T>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; }
}
}
namespace Azure.AI.OpenAI
{
public partial class AudioTranscription
Expand Down Expand Up @@ -114,6 +127,8 @@ public partial class AzureChatExtensionsMessageContext
{
public AzureChatExtensionsMessageContext() { }
public System.Collections.Generic.IList<Azure.AI.OpenAI.ChatMessage> Messages { get { throw null; } }
public Azure.AI.OpenAI.ContentFilterResults RequestContentFilterResults { get { throw null; } }
public Azure.AI.OpenAI.ContentFilterResults ResponseContentFilterResults { get { throw null; } }
}
public partial class AzureChatExtensionsOptions
{
Expand Down Expand Up @@ -204,10 +219,7 @@ public static partial class AzureOpenAIModelFactory
public static Azure.AI.OpenAI.ImageGenerations ImageGenerations(System.DateTimeOffset created = default(System.DateTimeOffset), System.Collections.Generic.IEnumerable<Azure.AI.OpenAI.ImageLocation> data = null) { throw null; }
public static Azure.AI.OpenAI.ImageLocation ImageLocation(System.Uri url = null) { throw null; }
public static Azure.AI.OpenAI.PromptFilterResult PromptFilterResult(int promptIndex = 0, Azure.AI.OpenAI.ContentFilterResults contentFilterResults = null) { throw null; }
public static Azure.AI.OpenAI.StreamingChatChoice StreamingChatChoice(Azure.AI.OpenAI.ChatChoice originalBaseChoice = null) { throw null; }
public static Azure.AI.OpenAI.StreamingChatCompletions StreamingChatCompletions(Azure.AI.OpenAI.ChatCompletions baseChatCompletions = null, System.Collections.Generic.List<Azure.AI.OpenAI.StreamingChatChoice> streamingChatChoices = null) { throw null; }
public static Azure.AI.OpenAI.StreamingChoice StreamingChoice(Azure.AI.OpenAI.Choice originalBaseChoice = null) { throw null; }
public static Azure.AI.OpenAI.StreamingCompletions StreamingCompletions(Azure.AI.OpenAI.Completions baseCompletions = null, System.Collections.Generic.List<Azure.AI.OpenAI.StreamingChoice> streamingChoices = null) { throw null; }
public static Azure.AI.OpenAI.StreamingChatCompletionsUpdate StreamingChatCompletionsUpdate(string id, System.DateTimeOffset created, int? choiceIndex = default(int?), Azure.AI.OpenAI.ChatRole? role = default(Azure.AI.OpenAI.ChatRole?), string authorName = null, string contentUpdate = null, Azure.AI.OpenAI.CompletionsFinishReason? finishReason = default(Azure.AI.OpenAI.CompletionsFinishReason?), string functionName = null, string functionArgumentsUpdate = null, Azure.AI.OpenAI.AzureChatExtensionsMessageContext azureExtensionsContext = null) { throw null; }
}
public partial class ChatChoice
{
Expand Down Expand Up @@ -478,14 +490,14 @@ public OpenAIClient(System.Uri endpoint, Azure.Core.TokenCredential tokenCredent
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.AudioTranslation>> GetAudioTranslationAsync(string deploymentId, Azure.AI.OpenAI.AudioTranslationOptions audioTranslationOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.ChatCompletions> GetChatCompletions(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.ChatCompletions>> GetChatCompletionsAsync(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.StreamingChatCompletions> GetChatCompletionsStreaming(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.StreamingChatCompletions>> GetChatCompletionsStreamingAsync(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.StreamingResponse<Azure.AI.OpenAI.StreamingChatCompletionsUpdate> GetChatCompletionsStreaming(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.StreamingResponse<Azure.AI.OpenAI.StreamingChatCompletionsUpdate>> GetChatCompletionsStreamingAsync(string deploymentOrModelName, Azure.AI.OpenAI.ChatCompletionsOptions chatCompletionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.Completions> GetCompletions(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.Completions> GetCompletions(string deploymentOrModelName, string prompt, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.Completions>> GetCompletionsAsync(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.Completions>> GetCompletionsAsync(string deploymentOrModelName, string prompt, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.StreamingCompletions> GetCompletionsStreaming(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.StreamingCompletions>> GetCompletionsStreamingAsync(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.StreamingResponse<Azure.AI.OpenAI.Completions> GetCompletionsStreaming(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.StreamingResponse<Azure.AI.OpenAI.Completions>> GetCompletionsStreamingAsync(string deploymentOrModelName, Azure.AI.OpenAI.CompletionsOptions completionsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.Embeddings> GetEmbeddings(string deploymentOrModelName, Azure.AI.OpenAI.EmbeddingsOptions embeddingsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.AI.OpenAI.Embeddings>> GetEmbeddingsAsync(string deploymentOrModelName, Azure.AI.OpenAI.EmbeddingsOptions embeddingsOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.AI.OpenAI.ImageGenerations> GetImageGenerations(Azure.AI.OpenAI.ImageGenerationOptions imageGenerationOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
Expand All @@ -510,42 +522,19 @@ internal PromptFilterResult() { }
public Azure.AI.OpenAI.ContentFilterResults ContentFilterResults { get { throw null; } }
public int PromptIndex { get { throw null; } }
}
public partial class StreamingChatChoice
public partial class StreamingChatCompletionsUpdate
{
internal StreamingChatChoice() { }
public Azure.AI.OpenAI.ContentFilterResults ContentFilterResults { get { throw null; } }
public Azure.AI.OpenAI.CompletionsFinishReason? FinishReason { get { throw null; } }
public int? Index { get { throw null; } }
public System.Collections.Generic.IAsyncEnumerable<Azure.AI.OpenAI.ChatMessage> GetMessageStreaming([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public partial class StreamingChatCompletions : System.IDisposable
{
internal StreamingChatCompletions() { }
internal StreamingChatCompletionsUpdate() { }
public string AuthorName { get { throw null; } }
public Azure.AI.OpenAI.AzureChatExtensionsMessageContext AzureExtensionsContext { get { throw null; } }
public int? ChoiceIndex { get { throw null; } }
public string ContentUpdate { get { throw null; } }
public System.DateTimeOffset Created { get { throw null; } }
public string Id { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<Azure.AI.OpenAI.PromptFilterResult> PromptFilterResults { get { throw null; } }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.Generic.IAsyncEnumerable<Azure.AI.OpenAI.StreamingChatChoice> GetChoicesStreaming([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public partial class StreamingChoice
{
internal StreamingChoice() { }
public Azure.AI.OpenAI.ContentFilterResults ContentFilterResults { get { throw null; } }
public Azure.AI.OpenAI.CompletionsFinishReason? FinishReason { get { throw null; } }
public int? Index { get { throw null; } }
public Azure.AI.OpenAI.CompletionsLogProbabilityModel LogProbabilityModel { get { throw null; } }
public System.Collections.Generic.IAsyncEnumerable<string> GetTextStreaming([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public partial class StreamingCompletions : System.IDisposable
{
internal StreamingCompletions() { }
public System.DateTimeOffset Created { get { throw null; } }
public string FunctionArgumentsUpdate { get { throw null; } }
public string FunctionName { get { throw null; } }
public string Id { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<Azure.AI.OpenAI.PromptFilterResult> PromptFilterResults { get { throw null; } }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.Generic.IAsyncEnumerable<Azure.AI.OpenAI.StreamingChoice> GetChoicesStreaming([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public Azure.AI.OpenAI.ChatRole? Role { get { throw null; } }
}
}
namespace Microsoft.Extensions.Azure
Expand Down
2 changes: 1 addition & 1 deletion sdk/openai/Azure.AI.OpenAI/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/openai/Azure.AI.OpenAI",
"Tag": "net/openai/Azure.AI.OpenAI_52e82965d8"
"Tag": "net/openai/Azure.AI.OpenAI_86a53abc90"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// <auto-generated/>

#nullable disable

using System.Collections.Generic;
using Azure.Core;

namespace Azure.AI.OpenAI
{
/// <summary>
/// A representation of the additional context information available when Azure OpenAI chat extensions are involved
/// in the generation of a corresponding chat completions response. This context information is only populated when
/// using an Azure OpenAI request configured to use a matching extension.
/// </summary>
public partial class AzureChatExtensionsMessageContext
{
public ContentFilterResults RequestContentFilterResults { get; internal set; }
public ContentFilterResults ResponseContentFilterResults { get; internal set; }

internal AzureChatExtensionsMessageContext(
IList<ChatMessage> messages,
ContentFilterResults requestContentFilterResults,
ContentFilterResults responseContentFilterResults)
: this(messages)
{
RequestContentFilterResults = requestContentFilterResults;
ResponseContentFilterResults = responseContentFilterResults;
}
}
}
71 changes: 25 additions & 46 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/AzureOpenAIModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ namespace Azure.AI.OpenAI
public static partial class AzureOpenAIModelFactory
{
/// <summary> Initializes a new instance of ChatChoice. </summary>
/// <param name="message"> The chat message associated with this chat completions choice </param>
/// <param name="message"> The chat message associated with this chat completions choice. </param>
/// <param name="index"> The ordered index associated with this chat completions choice. </param>
/// <param name="finishReason"> The reason that this chat completions choice completed its generated. </param>
/// <param name="deltaMessage"> For streamed choices, the internal representation of a 'delta' payload </param>
/// <param name="contentFilterResults"> The category annotations for this chat choice's content filtering </param>
/// <param name="deltaMessage"> For streamed choices, the internal representation of a 'delta' payload. </param>
/// <param name="contentFilterResults"> The category annotations for this chat choice's content filtering. </param>
/// <returns> A new <see cref="OpenAI.ChatChoice"/> instance for mocking. </returns>
public static ChatChoice ChatChoice(
ChatMessage message = null,
Expand All @@ -30,50 +30,29 @@ public static ChatChoice ChatChoice(
return new ChatChoice(message, index, finishReason, deltaMessage, contentFilterResults);
}

/// <summary>
/// Initializes a new instance of StreamingChoice for tests and mocking.
/// </summary>
/// <param name="originalBaseChoice"> An underlying Choice for this streaming representation </param>
/// <returns> A new instance of StreamingChoice </returns>
public static StreamingChoice StreamingChoice(Choice originalBaseChoice = null)
public static StreamingChatCompletionsUpdate StreamingChatCompletionsUpdate(
string id,
DateTimeOffset created,
int? choiceIndex = null,
ChatRole? role = null,
string authorName = null,
string contentUpdate = null,
CompletionsFinishReason? finishReason = null,
string functionName = null,
string functionArgumentsUpdate = null,
AzureChatExtensionsMessageContext azureExtensionsContext = null)
{
return new StreamingChoice(originalBaseChoice);
}

/// <summary>
/// Initializes a new instance of StreamingCompletions for tests and mocking.
/// </summary>
/// <param name="baseCompletions"> The non-streaming completions to base this streaming representation on </param>
/// <param name="streamingChoices"> The streaming choices associated with this streaming completions </param>
/// <returns> A new instance of StreamingCompletions </returns>
public static StreamingCompletions StreamingCompletions(
Completions baseCompletions = null,
List<StreamingChoice> streamingChoices = null)
{
return new StreamingCompletions(baseCompletions, streamingChoices);
}

/// <summary>
/// Initializes a new instance of StreamingChatChoice for tests and mocking.
/// </summary>
/// <param name="originalBaseChoice"> An underlying ChatChoice for this streaming representation </param>
/// <returns> A new instance of StreamingChatChoice </returns>
public static StreamingChatChoice StreamingChatChoice(ChatChoice originalBaseChoice = null)
{
return new StreamingChatChoice(originalBaseChoice);
}

/// <summary>
/// Initializes a new instance of StreamingChatCompletions for tests and mocking.
/// </summary>
/// <param name="baseChatCompletions"> The non-streaming completions to base this streaming representation on </param>
/// <param name="streamingChatChoices"> The streaming choices associated with this streaming chat completions </param>
/// <returns> A new instance of StreamingChatCompletions </returns>
public static StreamingChatCompletions StreamingChatCompletions(
ChatCompletions baseChatCompletions = null,
List<StreamingChatChoice> streamingChatChoices = null)
{
return new StreamingChatCompletions(baseChatCompletions, streamingChatChoices);
return new StreamingChatCompletionsUpdate(
id,
created,
choiceIndex,
role,
authorName,
contentUpdate,
finishReason,
functionName,
functionArgumentsUpdate,
azureExtensionsContext);
}

/// <summary> Initializes a new instance of AudioTranscription. </summary>
Expand Down
Loading