diff --git a/README.md b/README.md index 3312eecd..3761fb03 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 @@ -98,22 +94,19 @@ 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 chatUpdates - = client.CompleteChatStreaming( - [ - new UserChatMessage("Say 'this is a test.'"), - ]); +ResultCollection updates + = client.CompleteChatStreaming("Say 'this is a test.'"); ``` Notice that the returned value is a `ResultCollection` 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); } } ``` @@ -121,18 +114,15 @@ foreach (StreamingChatCompletionUpdate chatUpdate in chatUpdates) Alternatively, you can do this asynchronously by calling the `CompleteChatStreamingAsync` method to get an `AsyncResultCollection` and enumerate it using `await foreach`: ```csharp -AsyncResultCollection asyncChatUpdates - = client.CompleteChatStreamingAsync( - [ - new UserChatMessage("Say 'this is a test.'"), - ]); +AsyncResultCollection 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); } } ``` @@ -494,13 +484,7 @@ Next, create a new thread. For illustrative purposes, you could include an initi ```csharp ThreadCreationOptions threadOptions = new() { - InitialMessages = - { - new ThreadInitializationMessage(new List() - { - MessageContent.FromText("How well did product 113045 sell in February? Graph its trend over time."), - }), - }, + InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." } }; ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Id, threadOptions); diff --git a/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs b/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs index b6f16f8c..fb9189ac 100644 --- a/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs +++ b/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs @@ -85,13 +85,7 @@ public void Example01_RetrievalAugmentedGeneration() // Now we'll create a thread with a user query about the data already associated with the assistant, then run it ThreadCreationOptions threadOptions = new() { - InitialMessages = - { - new ThreadInitializationMessage(new List() - { - MessageContent.FromText("How well did product 113045 sell in February? Graph its trend over time."), - }), - }, + InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." } }; ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Id, threadOptions); diff --git a/examples/Assistants/Example02_FunctionCalling.cs b/examples/Assistants/Example02_FunctionCalling.cs index f3e3f464..c1cd91ca 100644 --- a/examples/Assistants/Example02_FunctionCalling.cs +++ b/examples/Assistants/Example02_FunctionCalling.cs @@ -82,7 +82,7 @@ string GetCurrentWeather(string location, string unit = "celsius") // Create a thread with an initial user message and run it. ThreadCreationOptions threadOptions = new() { - InitialMessages = { new ThreadInitializationMessage(["What's the weather like today?"]), }, + InitialMessages = { "What's the weather like today?" } }; ThreadRun run = client.CreateThreadAndRun(assistant.Id, threadOptions); diff --git a/examples/Assistants/Example02_FunctionCallingAsync.cs b/examples/Assistants/Example02_FunctionCallingAsync.cs index f0df13dc..9cbb3de8 100644 --- a/examples/Assistants/Example02_FunctionCallingAsync.cs +++ b/examples/Assistants/Example02_FunctionCallingAsync.cs @@ -82,7 +82,7 @@ string GetCurrentWeather(string location, string unit = "celsius") // Create a thread with an initial user message and run it. ThreadCreationOptions threadOptions = new() { - InitialMessages = { new ThreadInitializationMessage(["What's the weather like today?"]), }, + InitialMessages = { "What's the weather like today?" } }; ThreadRun run = await client.CreateThreadAndRunAsync(assistant.Id, threadOptions); diff --git a/examples/Assistants/Example04_AllTheTools.cs b/examples/Assistants/Example04_AllTheTools.cs index 7d0e371d..8ffbaa4c 100644 --- a/examples/Assistants/Example04_AllTheTools.cs +++ b/examples/Assistants/Example04_AllTheTools.cs @@ -83,13 +83,10 @@ static string GetNameOfFamilyMember(string relation) { InitialMessages = { - new ThreadInitializationMessage( - [ - "Create a graph of a line with a slope that's my father's favorite number " - + "and an offset that's my mother's favorite number.", - "Include people's names in your response and cite where you found them." - ]), - }, + "Create a graph of a line with a slope that's my father's favorite number " + + "and an offset that's my mother's favorite number.", + "Include people's names in your response and cite where you found them." + } }); ThreadRun run = client.CreateRun(thread, assistant); diff --git a/examples/Chat/Example01_SimpleChat.cs b/examples/Chat/Example01_SimpleChat.cs index c964f85a..3715532d 100644 --- a/examples/Chat/Example01_SimpleChat.cs +++ b/examples/Chat/Example01_SimpleChat.cs @@ -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}"); } } diff --git a/examples/Chat/Example01_SimpleChatAsync.cs b/examples/Chat/Example01_SimpleChatAsync.cs index 94343b88..14b61d1b 100644 --- a/examples/Chat/Example01_SimpleChatAsync.cs +++ b/examples/Chat/Example01_SimpleChatAsync.cs @@ -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}"); } } diff --git a/examples/Chat/Example02_SimpleChatStreaming.cs b/examples/Chat/Example02_SimpleChatStreaming.cs index 9e62dfec..cad64d9b 100644 --- a/examples/Chat/Example02_SimpleChatStreaming.cs +++ b/examples/Chat/Example02_SimpleChatStreaming.cs @@ -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 chatUpdates - = client.CompleteChatStreaming( - [ - new UserChatMessage("Say 'this is a test.'"), - ]); + ResultCollection 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); } } } diff --git a/examples/Chat/Example02_SimpleChatStreamingAsync.cs b/examples/Chat/Example02_SimpleChatStreamingAsync.cs index 9ca24a18..123b5e88 100644 --- a/examples/Chat/Example02_SimpleChatStreamingAsync.cs +++ b/examples/Chat/Example02_SimpleChatStreamingAsync.cs @@ -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 asyncChatUpdates - = client.CompleteChatStreamingAsync( - [ - new UserChatMessage("Say 'this is a test.'"), - ]); + AsyncResultCollection 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); } } } diff --git a/src/Custom/Assistants/ThreadInitializationMessage.cs b/src/Custom/Assistants/ThreadInitializationMessage.cs index 9cdbe11f..68f14ff5 100644 --- a/src/Custom/Assistants/ThreadInitializationMessage.cs +++ b/src/Custom/Assistants/ThreadInitializationMessage.cs @@ -16,4 +16,15 @@ public ThreadInitializationMessage(IEnumerable content) : base(c internal ThreadInitializationMessage(MessageCreationOptions baseOptions) : base(baseOptions.Role, baseOptions.Content, baseOptions.Attachments, baseOptions.Metadata, null) { } + + /// + /// Implicitly creates a new instance of from a single item of plain text + /// content. + /// + /// + /// Using a in the position of a is equivalent to + /// using the constructor with a single + /// content instance. + /// + public static implicit operator ThreadInitializationMessage(string initializationMessage) => new([initializationMessage]); } diff --git a/src/Custom/Chat/ChatClient.cs b/src/Custom/Chat/ChatClient.cs index abc04ab5..68e5e70b 100644 --- a/src/Custom/Chat/ChatClient.cs +++ b/src/Custom/Chat/ChatClient.cs @@ -81,6 +81,14 @@ public virtual async Task> CompleteChatAsync(IEnume return ClientResult.FromValue(ChatCompletion.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } + /// + /// Generates a single chat completion result for a provided set of input chat messages. + /// + /// The messages to provide as input and history for chat completion. + /// A result for a single chat completion. + public virtual async Task> CompleteChatAsync(params ChatMessage[] messages) + => await CompleteChatAsync(messages, default(ChatCompletionOptions)).ConfigureAwait(false); + /// /// Generates a single chat completion result for a provided set of input chat messages. /// @@ -100,6 +108,14 @@ public virtual ClientResult CompleteChat(IEnumerable + /// Generates a single chat completion result for a provided set of input chat messages. + /// + /// The messages to provide as input and history for chat completion. + /// A result for a single chat completion. + public virtual ClientResult CompleteChat(params ChatMessage[] messages) + => CompleteChat(messages, default(ChatCompletionOptions)); + /// /// Begins a streaming response for a chat completion request using the provided chat messages as input and /// history. @@ -125,6 +141,19 @@ async Task getResultAsync() => return new AsyncStreamingChatCompletionUpdateCollection(getResultAsync); } + /// + /// Begins a streaming response for a chat completion request using the provided chat messages as input and + /// history. + /// + /// + /// can be enumerated over using the await foreach pattern using the + /// interface. + /// + /// The messages to provide as input for chat completion. + /// A streaming result with incremental chat completion updates. + public virtual AsyncResultCollection CompleteChatStreamingAsync(params ChatMessage[] messages) + => CompleteChatStreamingAsync(messages, default(ChatCompletionOptions)); + /// /// Begins a streaming response for a chat completion request using the provided chat messages as input and /// history. @@ -149,6 +178,19 @@ public virtual ResultCollection CompleteChatStrea return new StreamingChatCompletionUpdateCollection(getResult); } + /// + /// Begins a streaming response for a chat completion request using the provided chat messages as input and + /// history. + /// + /// + /// can be enumerated over using the foreach pattern using the + /// interface. + /// + /// The messages to provide as input for chat completion. + /// A streaming result with incremental chat completion updates. + public virtual ResultCollection CompleteChatStreaming(params ChatMessage[] messages) + => CompleteChatStreaming(messages, default(ChatCompletionOptions)); + private void CreateChatCompletionOptions(IEnumerable messages, ref ChatCompletionOptions options, bool stream = false) { options.Messages = messages.ToList(); diff --git a/src/Custom/Chat/ChatCompletion.cs b/src/Custom/Chat/ChatCompletion.cs index f3452380..0efd2e53 100644 --- a/src/Custom/Chat/ChatCompletion.cs +++ b/src/Custom/Chat/ChatCompletion.cs @@ -60,4 +60,10 @@ public partial class ChatCompletion // CUSTOM: Flattened choice message property. public ChatFunctionCall FunctionCall => Choices[0].Message.FunctionCall; + + /// + /// Returns text representation of the first part of the first choice. + /// + /// + public override string ToString() => Content[0].Text; } diff --git a/src/Custom/Chat/ChatMessage.cs b/src/Custom/Chat/ChatMessage.cs index 13236a2f..e523eadd 100644 --- a/src/Custom/Chat/ChatMessage.cs +++ b/src/Custom/Chat/ChatMessage.cs @@ -89,4 +89,10 @@ public abstract partial class ChatMessage /// public static FunctionChatMessage CreateFunctionMessage(string functionName, string content) => new FunctionChatMessage(functionName, content); + + /// + /// Creates UserChatMessage. + /// + /// + public static implicit operator ChatMessage(string userMessage) => new UserChatMessage(userMessage); } diff --git a/src/Custom/Chat/ChatMessageContentPart.cs b/src/Custom/Chat/ChatMessageContentPart.cs index 2a77873a..f76b7842 100644 --- a/src/Custom/Chat/ChatMessageContentPart.cs +++ b/src/Custom/Chat/ChatMessageContentPart.cs @@ -124,4 +124,20 @@ public static ChatMessageContentPart CreateImageMessageContentPart(BinaryData im return new(imageBytes, imageBytesMediaType, imageDetail); } + + /// + /// Returns text representation of this part. + /// + /// + public override string ToString() => Text; + + /// + /// Implicitly creates a new instance from an item of plain text. + /// + /// + /// Using a in the position of a is equivalent to + /// calling the method. + /// + /// The text content to use as this content part. + public static implicit operator ChatMessageContentPart(string content) => new(content); }