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

Added helper APIs to simplify the basic getting-started usage #17

Merged
merged 2 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Next Next commit
simplified the basic getting-started usage
  • Loading branch information
KrzysztofCwalina committed Jun 6, 2024
commit 79ed7689ecbb66ccc73dbbbfe4a18ec68e986ed4
38 changes: 14 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ using OpenAI.Chat;

ChatClient client = new(model: "gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ChatCompletion chatCompletion = client.CompleteChat(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ChatCompletion completion = client.CompleteChat("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]: {completion}");
```

While you can pass your API key directly as a string, it is highly recommended to keep it in a secure location and instead access it via an environment variable or configuration file as shown above to avoid storing it in source control.
Expand Down Expand Up @@ -62,10 +61,7 @@ The library is organized into several namespaces corresponding to OpenAI feature
Every client method that performs a synchronous API call has an asynchronous variant in the same client class. For instance, the asynchronous variant of the `ChatClient`'s `CompleteChat` method is `CompleteChatAsync`. To rewrite the call above using the asynchronous counterpart, simply `await` the call to the corresponding async variant:

```csharp
ChatCompletion chatCompletion = await client.CompleteChatAsync(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'");
```

### Using the `OpenAIClient` class
Expand Down Expand Up @@ -98,41 +94,35 @@ When you request a chat completion, the default behavior is for the server to ge
The client library offers a convenient approach to working with streaming chat completions. If you wanted to re-write the example from the previous section using streaming, rather than calling the `ChatClient`'s `CompleteChat` method, you would call its `CompleteChatStreaming` method instead:

```csharp
ResultCollection<StreamingChatCompletionUpdate> chatUpdates
= client.CompleteChatStreaming(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ResultCollection<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreaming("Say 'this is a test.'");
```

Notice that the returned value is a `ResultCollection<StreamingChatCompletionUpdate>` instance, which can be enumerated to process the streaming response chunks as they arrive:

```csharp
Console.WriteLine($"[ASSISTANT]:");
foreach (StreamingChatCompletionUpdate chatUpdate in chatUpdates)
foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart contentPart in chatUpdate.ContentUpdate)
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
Console.Write(updatePart);
}
}
```

Alternatively, you can do this asynchronously by calling the `CompleteChatStreamingAsync` method to get an `AsyncResultCollection<StreamingChatCompletionUpdate>` and enumerate it using `await foreach`:

```csharp
AsyncResultCollection<StreamingChatCompletionUpdate> asyncChatUpdates
= client.CompleteChatStreamingAsync(
[
new UserChatMessage("Say 'this is a test.'"),
]);
AsyncResultCollection<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreamingAsync("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
await foreach (StreamingChatCompletionUpdate chatUpdate in asyncChatUpdates)
await foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart contentPart in chatUpdate.ContentUpdate)
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
Console.Write(updatePart.Text);
}
}
```
Expand Down
10 changes: 3 additions & 7 deletions examples/Chat/Example01_SimpleChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@ public partial class ChatExamples
[Test]
public void Example01_SimpleChat()
{
ChatClient client = new("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatClient client = new(model: "gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ChatCompletion chatCompletion = client.CompleteChat(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ChatCompletion completion = client.CompleteChat("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
Console.WriteLine($"{chatCompletion.Content[0].Text}");
Console.WriteLine($"[ASSISTANT]: {completion}");
}
}
10 changes: 3 additions & 7 deletions examples/Chat/Example01_SimpleChatAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ public partial class ChatExamples
[Test]
public async Task Example01_SimpleChatAsync()
{
ChatClient client = new("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatClient client = new(model: "gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ChatCompletion chatCompletion = await client.CompleteChatAsync(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
Console.WriteLine($"{chatCompletion.Content[0].Text}");
Console.WriteLine($"{completion}");
}
}
15 changes: 6 additions & 9 deletions examples/Chat/Example02_SimpleChatStreaming.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,17 @@ public partial class ChatExamples
[Test]
public void Example02_SimpleChatStreaming()
{
ChatClient client = new("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatClient client = new(model: "gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ResultCollection<StreamingChatCompletionUpdate> chatUpdates
= client.CompleteChatStreaming(
[
new UserChatMessage("Say 'this is a test.'"),
]);
ResultCollection<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreaming("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
foreach (StreamingChatCompletionUpdate chatUpdate in chatUpdates)
foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart contentPart in chatUpdate.ContentUpdate)
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
Console.Write(updatePart);
}
}
}
Expand Down
15 changes: 6 additions & 9 deletions examples/Chat/Example02_SimpleChatStreamingAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,17 @@ public partial class ChatExamples
[Test]
public async Task Example02_SimpleChatStreamingAsync()
{
ChatClient client = new("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatClient client = new(model: "gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

AsyncResultCollection<StreamingChatCompletionUpdate> asyncChatUpdates
= client.CompleteChatStreamingAsync(
[
new UserChatMessage("Say 'this is a test.'"),
]);
AsyncResultCollection<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreamingAsync("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
await foreach (StreamingChatCompletionUpdate chatUpdate in asyncChatUpdates)
await foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart contentPart in chatUpdate.ContentUpdate)
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
Console.Write(updatePart.Text);
}
}
}
Expand Down
42 changes: 42 additions & 0 deletions src/Custom/Chat/ChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(IEnume
return ClientResult.FromValue(ChatCompletion.FromResponse(result.GetRawResponse()), result.GetRawResponse());
}

/// <summary>
/// Generates a single chat completion result for a provided set of input chat messages.
/// </summary>
/// <param name="messages"> The messages to provide as input and history for chat completion. </param>
/// <returns> A result for a single chat completion. </returns>
public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(params ChatMessage[] messages)
=> await CompleteChatAsync(messages, default(ChatCompletionOptions)).ConfigureAwait(false);

/// <summary>
/// Generates a single chat completion result for a provided set of input chat messages.
/// </summary>
Expand All @@ -100,6 +108,14 @@ public virtual ClientResult<ChatCompletion> CompleteChat(IEnumerable<ChatMessage

}

/// <summary>
/// Generates a single chat completion result for a provided set of input chat messages.
/// </summary>
/// <param name="messages"> The messages to provide as input and history for chat completion. </param>
/// <returns> A result for a single chat completion. </returns>
public virtual ClientResult<ChatCompletion> CompleteChat(params ChatMessage[] messages)
=> CompleteChat(messages, default(ChatCompletionOptions));

/// <summary>
/// Begins a streaming response for a chat completion request using the provided chat messages as input and
/// history.
Expand All @@ -125,6 +141,19 @@ public virtual AsyncResultCollection<StreamingChatCompletionUpdate> CompleteChat
return new AsyncStreamingChatCompletionUpdateCollection(getResultAsync);
}

/// <summary>
/// Begins a streaming response for a chat completion request using the provided chat messages as input and
/// history.
/// </summary>
/// <remarks>
/// <see cref="AsyncResultCollection{T}"/> can be enumerated over using the <c>await foreach</c> pattern using the
/// <see cref="IAsyncEnumerable{T}"/> interface.
/// </remarks>
/// <param name="messages"> The messages to provide as input for chat completion. </param>
/// <returns> A streaming result with incremental chat completion updates. </returns>
public virtual AsyncResultCollection<StreamingChatCompletionUpdate> CompleteChatStreamingAsync(params ChatMessage[] messages)
=> CompleteChatStreamingAsync(messages, default(ChatCompletionOptions));

/// <summary>
/// Begins a streaming response for a chat completion request using the provided chat messages as input and
/// history.
Expand All @@ -149,6 +178,19 @@ public virtual ResultCollection<StreamingChatCompletionUpdate> CompleteChatStrea
return new StreamingChatCompletionUpdateCollection(getResult);
}

/// <summary>
/// Begins a streaming response for a chat completion request using the provided chat messages as input and
/// history.
/// </summary>
/// <remarks>
/// <see cref="ResultCollection{T}"/> can be enumerated over using the <c>foreach</c> pattern using the
/// <see cref="IEnumerable{T}"/> interface.
/// </remarks>
/// <param name="messages"> The messages to provide as input for chat completion. </param>
/// <returns> A streaming result with incremental chat completion updates. </returns>
public virtual ResultCollection<StreamingChatCompletionUpdate> CompleteChatStreaming(params ChatMessage[] messages)
=> CompleteChatStreaming(messages, default(ChatCompletionOptions));

private void CreateChatCompletionOptions(IEnumerable<ChatMessage> messages, ref ChatCompletionOptions options, bool stream = false)
{
options.Messages = messages.ToList();
Expand Down
6 changes: 6 additions & 0 deletions src/Custom/Chat/ChatCompletion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,10 @@ public partial class ChatCompletion

// CUSTOM: Flattened choice message property.
public ChatFunctionCall FunctionCall => Choices[0].Message.FunctionCall;

/// <summary>
/// Returns text representation of the first part of the first choice.
/// </summary>
/// <returns></returns>
public override string ToString() => Content[0].Text;
}
6 changes: 6 additions & 0 deletions src/Custom/Chat/ChatMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,11 @@
public static ToolChatMessage CreateToolChatMessage(string toolCallId, string content) => new ToolChatMessage(toolCallId, content);

/// <inheritdoc cref="FunctionChatMessage(string, string)"/>
public static FunctionChatMessage CreateFunctionMessage(string functionName, string content) => new FunctionChatMessage(functionName, content);

Check warning on line 91 in src/Custom/Chat/ChatMessage.cs

View workflow job for this annotation

GitHub Actions / build

'FunctionChatMessage' is obsolete: 'This field is marked as deprecated.'

/// <summary>
/// Creates UserChatMessage.
/// </summary>
/// <param name="userMessage"></param>
public static implicit operator ChatMessage(string userMessage) => new UserChatMessage(userMessage);
}
6 changes: 6 additions & 0 deletions src/Custom/Chat/ChatMessageContentPart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,10 @@ public static ChatMessageContentPart CreateImageMessageContentPart(BinaryData im

return new(imageBytes, imageBytesMediaType, imageDetail);
}

/// <summary>
/// Returns text representation of this part.
/// </summary>
/// <returns></returns>
public override string ToString() => Text;
}
Loading