diff --git a/README.md b/README.md
index a5cc89e..571c5c6 100644
--- a/README.md
+++ b/README.md
@@ -512,7 +512,7 @@ var functionResponse = await GetWeatherAsync(functionCall.Arguments);
await chatGptClient.AddToolResponseAsync(conversationId, tool, functionResponse);
```
-Check out the [Function calling sample](https://github.com/marcominerva/ChatGptNet/blob/master/samples/ChatGptFunctionCallingConsole/Application.cs#L18) for a complete implementation of this workflow.
+Finally, you need to resend the original message to the chat completion API, so that the model can continue the conversation taking into account the function call response. Check out the [Function calling sample](https://github.com/marcominerva/ChatGptNet/blob/master/samples/ChatGptFunctionCallingConsole/Application.cs#L18) for a complete implementation of this workflow.
## Content filtering
diff --git a/docs/ChatGptNet.Models/ChatGptToolParameters.md b/docs/ChatGptNet.Models/ChatGptToolParameters.md
index 38d32e1..e74e9fd 100644
--- a/docs/ChatGptNet.Models/ChatGptToolParameters.md
+++ b/docs/ChatGptNet.Models/ChatGptToolParameters.md
@@ -1,6 +1,6 @@
# ChatGptToolParameters class
-Contains parameters about the tools that are available for ChatGPT.
+Contains parameters about the tool calls that are available for ChatGPT.
```csharp
public class ChatGptToolParameters
diff --git a/docs/ChatGptNet/IChatGptClient.md b/docs/ChatGptNet/IChatGptClient.md
index 70ee002..640ed2b 100644
--- a/docs/ChatGptNet/IChatGptClient.md
+++ b/docs/ChatGptNet/IChatGptClient.md
@@ -10,7 +10,7 @@ public interface IChatGptClient
| name | description |
| --- | --- |
-| [AddInteractionAsync](IChatGptClient/AddInteractionAsync.md)(…) | Explicitly adds a new interaction (a question and the corresponding answer) to the conversation history. |
+| [AddInteractionAsync](IChatGptClient/AddInteractionAsync.md)(…) | Explicitly adds a new interaction (a question and the corresponding answer) to an existing conversation history. |
| [AddToolResponseAsync](IChatGptClient/AddToolResponseAsync.md)(…) | Adds a function response to the conversation history. (4 methods) |
| [AskAsync](IChatGptClient/AskAsync.md)(…) | Requests a new chat interaction. (4 methods) |
| [AskStreamAsync](IChatGptClient/AskStreamAsync.md)(…) | Requests a new chat interaction with streaming response, like in ChatGPT. (2 methods) |
diff --git a/docs/ChatGptNet/IChatGptClient/AddInteractionAsync.md b/docs/ChatGptNet/IChatGptClient/AddInteractionAsync.md
index 7b4fabb..001e40d 100644
--- a/docs/ChatGptNet/IChatGptClient/AddInteractionAsync.md
+++ b/docs/ChatGptNet/IChatGptClient/AddInteractionAsync.md
@@ -1,6 +1,6 @@
# IChatGptClient.AddInteractionAsync method
-Explicitly adds a new interaction (a question and the corresponding answer) to the conversation history.
+Explicitly adds a new interaction (a question and the corresponding answer) to an existing conversation history.
```csharp
public Task AddInteractionAsync(Guid conversationId, string question, string answer,
@@ -22,6 +22,7 @@ The Task corresponding to the asynchronous operation.
| exception | condition |
| --- | --- |
+| ArgumentException | *conversationId* is Empty. |
| ArgumentNullException | *question* or *answer* are `null`. |
## See Also
diff --git a/docs/ChatGptNet/IChatGptClient/AddToolResponseAsync.md b/docs/ChatGptNet/IChatGptClient/AddToolResponseAsync.md
index a007aff..e03a038 100644
--- a/docs/ChatGptNet/IChatGptClient/AddToolResponseAsync.md
+++ b/docs/ChatGptNet/IChatGptClient/AddToolResponseAsync.md
@@ -22,6 +22,7 @@ The Task corresponding to the asynchronous operation.
| exception | condition |
| --- | --- |
+| ArgumentException | *conversationId* is Empty. |
| ArgumentNullException | [`Name`](../../ChatGptNet.Models/ChatGptFunction/Name.md) or *content* are `null`. |
| InvalidOperationException | The conversation history is empty. |
diff --git a/docs/README.md b/docs/README.md
index 0f1a2ad..8b26f5e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -55,7 +55,7 @@
| class [ChatGptTool](./ChatGptNet.Models/ChatGptTool.md) | Represents a tool that the model may call. |
| class [ChatGptToolCall](./ChatGptNet.Models/ChatGptToolCall.md) | A tool call generated by the model, such as a function call. |
| static class [ChatGptToolChoices](./ChatGptNet.Models/ChatGptToolChoices.md) | Contains constants for ChatGPT function call types. |
-| class [ChatGptToolParameters](./ChatGptNet.Models/ChatGptToolParameters.md) | Contains parameters about the function calls that are available for ChatGPT. |
+| class [ChatGptToolParameters](./ChatGptNet.Models/ChatGptToolParameters.md) | Contains parameters about the tools calls that are available for ChatGPT. |
| static class [ChatGptToolTypes](./ChatGptNet.Models/ChatGptToolTypes.md) | Contains constants for ChatGPT tool types. |
| class [ChatGptUsage](./ChatGptNet.Models/ChatGptUsage.md) | Contains information about the API usage. |
| static class [OpenAIChatGptModels](./ChatGptNet.Models/OpenAIChatGptModels.md) | Contains all the chat completion models that are currently supported by OpenAI. |
diff --git a/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj b/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj
index f51b787..ca361a7 100644
--- a/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj
+++ b/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/samples/ChatGptFunctionCallingConsole/Application.cs b/samples/ChatGptFunctionCallingConsole/Application.cs
index 5db0b0f..d1b751b 100644
--- a/samples/ChatGptFunctionCallingConsole/Application.cs
+++ b/samples/ChatGptFunctionCallingConsole/Application.cs
@@ -5,15 +5,8 @@
namespace ChatGptConsole;
-internal class Application
+internal class Application(IChatGptClient chatGptClient)
{
- private readonly IChatGptClient chatGptClient;
-
- public Application(IChatGptClient chatGptClient)
- {
- this.chatGptClient = chatGptClient;
- }
-
public async Task ExecuteAsync()
{
Console.WriteLine("Welcome! You can ask me whatever you want, but if you ask me something about the weather, I will probably suggest you to call a function.");
@@ -107,15 +100,12 @@ public async Task ExecuteAsync()
var functionCall = response.GetFunctionCall()!;
Console.ForegroundColor = ConsoleColor.Green;
-
Console.WriteLine(functionCall.Name);
Console.WriteLine(functionCall.Arguments);
-
Console.ResetColor();
- // Simulate the calling to the function.
+ // Simulates the call to the function.
var functionResponse = await GetWeatherAsync(functionCall.GetArgumentsAsJson());
- Console.WriteLine(functionResponse);
// After the function has been called, it is necessary to add the response to the conversation.
@@ -127,6 +117,17 @@ public async Task ExecuteAsync()
// for example the gpt-4 1106-preview model, you need the following code:
//var tool = response.GetToolCalls()!.First();
//await chatGptClient.AddToolResponseAsync(conversationId, tool, functionResponse);
+
+ Console.WriteLine("The function gives the following response:");
+
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(functionResponse);
+ Console.ResetColor();
+
+ // Finally, it sends the original message back to the model, to obtain a response that takes into account the function call.
+ response = await chatGptClient.AskAsync(conversationId, message, toolParameters);
+
+ Console.WriteLine(response.GetContent());
}
else
{
@@ -150,14 +151,30 @@ public async Task ExecuteAsync()
private static Task GetWeatherAsync(JsonDocument? arguments)
{
- var summaries = new[]
- {
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
- };
+ string[] summaries =
+ [
+ "Freezing",
+ "Bracing",
+ "Chilly",
+ "Cool",
+ "Mild",
+ "Warm",
+ "Balmy",
+ "Hot",
+ "Sweltering",
+ "Scorching"
+ ];
var location = arguments?.RootElement.GetProperty("location").GetString();
- var response = $"It is {summaries[Random.Shared.Next(summaries.Length)]} in {location}, with {Random.Shared.Next(-20, 35)}° degrees";
+ var response = $$"""
+ {
+ "location": "{{location}}",
+ "temperature": {{Random.Shared.Next(-5, 35)}},
+ "description": "{{summaries[Random.Shared.Next(summaries.Length)]}}"
+ }
+ """;
+
return Task.FromResult(response);
}
}
diff --git a/src/ChatGptNet/ChatGptClient.cs b/src/ChatGptNet/ChatGptClient.cs
index c5c4ee6..591505d 100644
--- a/src/ChatGptNet/ChatGptClient.cs
+++ b/src/ChatGptNet/ChatGptClient.cs
@@ -246,12 +246,12 @@ public async Task LoadConversationAsync(Guid conversationId, IEnumerable();
- messages = messages.Union(new ChatGptMessage[]
- {
+ messages = messages.Union([
new()
{
Role = ChatGptRoles.User,
@@ -262,20 +262,21 @@ public async Task AddInteractionAsync(Guid conversationId, string question, stri
Role = ChatGptRoles.Assistant,
Content = answer
}
- });
+ ]);
await UpdateCacheAsync(conversationId, messages, cancellationToken);
}
public async Task AddToolResponseAsync(Guid conversationId, string? toolId, string name, string content, CancellationToken cancellationToken = default)
{
+ ThrowIfEmptyConversationId(conversationId, nameof(conversationId));
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(content);
var messages = await cache.GetAsync(conversationId, cancellationToken);
if (!messages?.Any() ?? true)
{
- throw new InvalidOperationException("Cannot add a function response message if the conversation history is empty");
+ throw new InvalidOperationException("Cannot add a tool/function response message if the conversation history is empty");
}
messages = messages!.Append(new()
@@ -338,7 +339,8 @@ private ChatGptRequest CreateChatGptRequest(IEnumerable messages
{ } => JsonDocument.Parse($$"""{ "type": "{{ChatGptToolTypes.Function}}", "{{ChatGptToolTypes.Function}}": { "name": "{{toolParameters.ToolChoice}}" } }"""),
_ => null
},
- // If the tool paramters uses the legacy function properties.
+
+ // If the tool parameters uses the legacy function properties.
Functions = toolParameters?.Functions,
FunctionCall = toolParameters?.FunctionCall switch
{
@@ -426,4 +428,12 @@ private static void NormalizeResponse(HttpResponseMessage httpResponse, Response
response.Error.StatusCode = (int)httpResponse.StatusCode;
}
}
+
+ private static void ThrowIfEmptyConversationId(Guid guid, string parameterName)
+ {
+ if (guid == Guid.Empty)
+ {
+ throw new ArgumentException($"The value {guid} is invalid", parameterName);
+ }
+ }
}
diff --git a/src/ChatGptNet/IChatGptClient.cs b/src/ChatGptNet/IChatGptClient.cs
index 45caf2f..fb5f710 100644
--- a/src/ChatGptNet/IChatGptClient.cs
+++ b/src/ChatGptNet/IChatGptClient.cs
@@ -155,13 +155,14 @@ IAsyncEnumerable AskStreamAsync(string message, ChatGptParamete
IAsyncEnumerable AskStreamAsync(Guid conversationId, string message, ChatGptParameters? parameters = null, string? model = null, bool addToConversationHistory = true, CancellationToken cancellationToken = default);
///
- /// Explicitly adds a new interaction (a question and the corresponding answer) to the conversation history.
+ /// Explicitly adds a new interaction (a question and the corresponding answer) to an existing conversation history.
///
/// The unique identifier of the conversation.
/// The question.
/// The answer.
/// The token to monitor for cancellation requests.
/// The corresponding to the asynchronous operation.
+ /// is .
/// or are .
Task AddInteractionAsync(Guid conversationId, string question, string answer, CancellationToken cancellationToken = default);
@@ -230,6 +231,7 @@ Task LoadConversationAsync(IEnumerable messages, Cancellat
/// The content of the function response.
/// The token to monitor for cancellation requests.
/// The corresponding to the asynchronous operation.
+ /// is .
/// or are .
/// The conversation history is empty.
///
diff --git a/src/ChatGptNet/Models/ChatGptToolParameters.cs b/src/ChatGptNet/Models/ChatGptToolParameters.cs
index e4bba16..2835f8b 100644
--- a/src/ChatGptNet/Models/ChatGptToolParameters.cs
+++ b/src/ChatGptNet/Models/ChatGptToolParameters.cs
@@ -1,7 +1,7 @@
namespace ChatGptNet.Models;
///
-/// Contains parameters about the tools calls that are available for ChatGPT.
+/// Contains parameters about the tool calls that are available for ChatGPT.
///
public class ChatGptToolParameters
{