Skip to content

Commit

Permalink
first steps
Browse files Browse the repository at this point in the history
  • Loading branch information
annelo-msft committed Mar 28, 2024
1 parent 4c355d5 commit 4f9e832
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 137 deletions.
67 changes: 29 additions & 38 deletions .dotnet/src/Custom/Chat/ChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ public ChatClient(string model, ApiKeyCredential credential = default, OpenAICli
/// <param name="message"> The user message to provide as a prompt for chat completion. </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <returns> A result for a single chat completion. </returns>
public virtual ClientResult<ChatCompletion> CompleteChat(string message, ChatCompletionOptions options = null)
=> CompleteChat(new List<ChatRequestMessage>() { new ChatRequestUserMessage(message) }, options);
public virtual ClientResult<ChatCompletion> CompleteChat(string message, ChatCompletionOptions options = null)
=> CompleteChat(new List<ChatRequestMessage>() { new ChatRequestUserMessage(message) }, options);

/// <summary>
/// Generates a single chat completion result for a single, simple user message.
/// </summary>
/// <param name="message"> The user message to provide as a prompt for chat completion. </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <returns> A result for a single chat completion. </returns>
public virtual Task<ClientResult<ChatCompletion>> CompleteChatAsync(string message, ChatCompletionOptions options = null)
=> CompleteChatAsync(
new List<ChatRequestMessage>() { new ChatRequestUserMessage(message) }, options);
public virtual Task<ClientResult<ChatCompletion>> CompleteChatAsync(string message, ChatCompletionOptions options = null)
=> CompleteChatAsync(
new List<ChatRequestMessage>() { new ChatRequestUserMessage(message) }, options);

/// <summary>
/// Generates a single chat completion result for a provided set of input chat messages.
Expand All @@ -63,7 +63,7 @@ public virtual ClientResult<ChatCompletion> CompleteChat(
IEnumerable<ChatRequestMessage> messages,
ChatCompletionOptions options = null)
{
Internal.Models.CreateChatCompletionRequest request = CreateInternalRequest(messages, options);
Internal.Models.CreateChatCompletionRequest request = CreateInternalRequest(messages, options);
ClientResult<Internal.Models.CreateChatCompletionResponse> response = Shim.CreateChatCompletion(request);
ChatCompletion chatCompletion = new(response.Value, internalChoiceIndex: 0);
return ClientResult.FromValue(chatCompletion, response.GetRawResponse());
Expand Down Expand Up @@ -93,7 +93,6 @@ public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(
/// The number of independent, alternative response choices that should be generated.
/// </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <param name="cancellationToken"> The cancellation token for the operation. </param>
/// <returns> A result for a single chat completion. </returns>
public virtual ClientResult<ChatCompletionCollection> CompleteChat(
IEnumerable<ChatRequestMessage> messages,
Expand Down Expand Up @@ -147,14 +146,14 @@ public virtual async Task<ClientResult<ChatCompletionCollection>> CompleteChatAs
/// </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <returns> A streaming result with incremental chat completion updates. </returns>
public virtual StreamingClientResult<StreamingChatUpdate> CompleteChatStreaming(
string message,
int? choiceCount = null,
ChatCompletionOptions options = null)
=> CompleteChatStreaming(
new List<ChatRequestMessage> { new ChatRequestUserMessage(message) },
choiceCount,
options);
public virtual StreamingClientResult<StreamingChatUpdate> CompleteChatStreaming(
string message,
int? choiceCount = null,
ChatCompletionOptions options = null)
=> CompleteChatStreaming(
new List<ChatRequestMessage> { new ChatRequestUserMessage(message) },
choiceCount,
options);

/// <summary>
/// Begins a streaming response for a chat completion request using a single, simple user message as input.
Expand Down Expand Up @@ -191,29 +190,25 @@ public virtual Task<StreamingClientResult<StreamingChatUpdate>> CompleteChatStre
/// The number of independent, alternative choices that the chat completion request should generate.
/// </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <param name="cancellationToken"> The cancellation token for the operation. </param>
/// <returns> A streaming result with incremental chat completion updates. </returns>
public virtual StreamingClientResult<StreamingChatUpdate> CompleteChatStreaming(
IEnumerable<ChatRequestMessage> messages,
int? choiceCount = null,
ChatCompletionOptions options = null)
{
PipelineMessage requestMessage = CreateCustomRequestMessage(messages, choiceCount, options);
requestMessage.BufferResponse = false;
Shim.Pipeline.Send(requestMessage);
PipelineResponse response = requestMessage.ExtractResponse();
PipelineMessage message = CreateCustomRequestMessage(messages, choiceCount, options);
message.BufferResponse = false;

Shim.Pipeline.Send(message);

PipelineResponse response = message.Response;

if (response.IsError)
{
throw new ClientResultException(response);
}

ClientResult genericResult = ClientResult.FromResponse(response);
return StreamingClientResult<StreamingChatUpdate>.CreateFromResponse(
genericResult,
(responseForEnumeration) => SseAsyncEnumerator<StreamingChatUpdate>.EnumerateFromSseJsonStream(
responseForEnumeration.GetRawResponse().ContentStream,
StreamingChatUpdate.DeserializeSseChatUpdates));
return new StreamingChatResult(response);

Check failure on line 211 in .dotnet/src/Custom/Chat/ChatClient.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StreamingChatResult' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 211 in .dotnet/src/Custom/Chat/ChatClient.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StreamingChatResult' could not be found (are you missing a using directive or an assembly reference?)
}

/// <summary>
Expand All @@ -229,29 +224,25 @@ public virtual StreamingClientResult<StreamingChatUpdate> CompleteChatStreaming(
/// The number of independent, alternative choices that the chat completion request should generate.
/// </param>
/// <param name="options"> Additional options for the chat completion request. </param>
/// <param name="cancellationToken"> The cancellation token for the operation. </param>
/// <returns> A streaming result with incremental chat completion updates. </returns>
public virtual async Task<StreamingClientResult<StreamingChatUpdate>> CompleteChatStreamingAsync(
IEnumerable<ChatRequestMessage> messages,
int? choiceCount = null,
ChatCompletionOptions options = null)
{
PipelineMessage requestMessage = CreateCustomRequestMessage(messages, choiceCount, options);
requestMessage.BufferResponse = false;
await Shim.Pipeline.SendAsync(requestMessage).ConfigureAwait(false);
PipelineResponse response = requestMessage.ExtractResponse();
PipelineMessage message = CreateCustomRequestMessage(messages, choiceCount, options);
message.BufferResponse = false;

await Shim.Pipeline.SendAsync(message).ConfigureAwait(false);

PipelineResponse response = message.Response;

if (response.IsError)
{
throw new ClientResultException(response);
}

ClientResult genericResult = ClientResult.FromResponse(response);
return StreamingClientResult<StreamingChatUpdate>.CreateFromResponse(
genericResult,
(responseForEnumeration) => SseAsyncEnumerator<StreamingChatUpdate>.EnumerateFromSseJsonStream(
responseForEnumeration.GetRawResponse().ContentStream,
StreamingChatUpdate.DeserializeSseChatUpdates));
return new StreamingChatResult(response);

Check failure on line 245 in .dotnet/src/Custom/Chat/ChatClient.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StreamingChatResult' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 245 in .dotnet/src/Custom/Chat/ChatClient.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StreamingChatResult' could not be found (are you missing a using directive or an assembly reference?)
}

private Internal.Models.CreateChatCompletionRequest CreateInternalRequest(
Expand Down Expand Up @@ -326,4 +317,4 @@ private PipelineMessage CreateCustomRequestMessage(IEnumerable<ChatRequestMessag

private static PipelineMessageClassifier _responseErrorClassifier200;
private static PipelineMessageClassifier ResponseErrorClassifier200 => _responseErrorClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 });
}
}
12 changes: 9 additions & 3 deletions .dotnet/src/Custom/Chat/StreamingChatUpdate.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace OpenAI.Chat;

using System;
using System.Collections.Generic;
using System.Text.Json;

namespace OpenAI.Chat;

/// <summary>
/// Represents an incremental item of new data in a streaming response to a chat completion request.
/// </summary>
Expand Down Expand Up @@ -184,11 +184,17 @@ internal StreamingChatUpdate(

internal static IEnumerable<StreamingChatUpdate> DeserializeSseChatUpdates(ReadOnlyMemory<char> _, JsonElement sseDataJson)
{
// TODO: would another enumerable implementation be more performant than list?
List<StreamingChatUpdate> results = [];

// TODO: Do we need to validate that we didn't get null or empty?
// What's the contract for the JSON updates?

if (sseDataJson.ValueKind == JsonValueKind.Null)
{
return results;
}

string id = default;
DateTimeOffset created = default;
string systemFingerprint = null;
Expand Down Expand Up @@ -333,4 +339,4 @@ Internal.Models.CreateChatCompletionResponseChoiceLogprobs internalLogprobs
}
return results;
}
}
}
8 changes: 7 additions & 1 deletion .dotnet/src/Utility/SseReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace OpenAI;

internal sealed class SseReader : IDisposable
internal sealed class SseReader : IDisposable, IAsyncDisposable
{
private readonly Stream _stream;
private readonly StreamReader _reader;
Expand Down Expand Up @@ -121,4 +121,10 @@ private void Dispose(bool disposing)
_disposedValue = true;
}
}

ValueTask IAsyncDisposable.DisposeAsync()
{
// TODO: revisit per platforms where async dispose is available.
return new ValueTask();
}
}
28 changes: 28 additions & 0 deletions .dotnet/src/Utility/StreamingClientResultOfT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Threading;

namespace OpenAI;

#pragma warning disable CS1591 // public XML comments

/// <summary>
/// Represents an operation response with streaming content that can be deserialized and enumerated while the response
/// is still being received.
/// </summary>
/// <typeparam name="T"> The data type representative of distinct, streamable items. </typeparam>
// TODO: Revisit the IDisposable question
public abstract class StreamingClientResult<T> : ClientResult, IAsyncEnumerable<T>
{
protected StreamingClientResult(PipelineResponse response) : base(response)
{
}

// Note that if the implementation disposes the stream, the caller can only
// enumerate the results once. I think this makes sense, but we should
// make sure architects agree.
public abstract IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}

#pragma warning restore CS1591 // public XML comments
95 changes: 0 additions & 95 deletions .dotnet/src/Utility/StreamingResult.cs

This file was deleted.

0 comments on commit 4f9e832

Please sign in to comment.