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

OpenAI-DotNet 7.3.2 #177

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
57 changes: 41 additions & 16 deletions OpenAI-DotNet-Tests/TestFixture_03_Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public async Task Test_01_GetChatCompletion()

foreach (var choice in response.Choices)
{
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice.Message.Content} | Finish Reason: {choice.FinishReason}");
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice} | Finish Reason: {choice.FinishReason}");
}

response.GetUsage();
Expand Down Expand Up @@ -65,8 +65,10 @@ public async Task Test_02_GetChatStreamingCompletion()
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
var choice = response.FirstChoice;
Assert.IsFalse(string.IsNullOrEmpty(choice?.Message?.Content));
Console.WriteLine($"[{choice!.Index}] {choice.Message!.Role}: {choice.Message.Content} | Finish Reason: {choice.FinishReason}");
Assert.IsNotNull(choice);
Assert.IsNotNull(choice.Message);
Assert.IsFalse(string.IsNullOrEmpty(choice.Message.Content));
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice} | Finish Reason: {choice.FinishReason}");
Assert.IsTrue(choice.Message.Role == Role.Assistant);
Assert.IsTrue(choice.Message.Content!.Equals(cumulativeDelta));
Console.WriteLine(response.ToString());
Expand Down Expand Up @@ -149,7 +151,7 @@ public async Task Test_04_GetChatFunctionCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Expand All @@ -164,7 +166,7 @@ public async Task Test_04_GetChatFunctionCompletion()

if (!string.IsNullOrEmpty(response.FirstChoice.Message.Content))
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var unitMessage = new Message(Role.User, "celsius");
messages.Add(unitMessage);
Expand Down Expand Up @@ -259,7 +261,7 @@ public async Task Test_05_GetChatFunctionCompletion_Streaming()

if (!string.IsNullOrEmpty(response.FirstChoice.Message.Content))
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var unitMessage = new Message(Role.User, "celsius");
messages.Add(unitMessage);
Expand Down Expand Up @@ -336,7 +338,7 @@ public async Task Test_06_GetChatFunctionForceCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Expand Down Expand Up @@ -411,7 +413,7 @@ public async Task Test_07_GetChatToolCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Expand All @@ -424,9 +426,9 @@ public async Task Test_07_GetChatToolCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

if (!string.IsNullOrEmpty(response.FirstChoice.Message.Content))
if (!string.IsNullOrEmpty(response.FirstChoice))
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var unitMessage = new Message(Role.User, "celsius");
messages.Add(unitMessage);
Expand Down Expand Up @@ -521,7 +523,7 @@ public async Task Test_08_GetChatToolCompletion_Streaming()

if (!string.IsNullOrEmpty(response.FirstChoice.Message.Content))
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var unitMessage = new Message(Role.User, "celsius");
messages.Add(unitMessage);
Expand Down Expand Up @@ -598,7 +600,7 @@ public async Task Test_09_GetChatToolForceCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Expand Down Expand Up @@ -637,14 +639,14 @@ public async Task Test_10_GetChatVision()
new Message(Role.User, new List<Content>
{
new Content(ContentType.Text, "What's in this image?"),
new Content(ContentType.ImageUrl, "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg")
new ImageUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", ImageDetail.Low)
})
};
var chatRequest = new ChatRequest(messages, model: "gpt-4-vision-preview");
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishDetails}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishDetails}");
response.GetUsage();
}

Expand All @@ -658,7 +660,7 @@ public async Task Test_11_GetChatVisionStreaming()
new Message(Role.User, new List<Content>
{
new Content(ContentType.Text, "What's in this image?"),
new Content(ContentType.ImageUrl, "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg")
new ImageUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", ImageDetail.Low)
})
};
var chatRequest = new ChatRequest(messages, model: "gpt-4-vision-preview");
Expand All @@ -670,7 +672,30 @@ public async Task Test_11_GetChatVisionStreaming()
});
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishDetails}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishDetails}");
response.GetUsage();
}

[Test]
public async Task Test_12_JsonMode()
{
Assert.IsNotNull(OpenAIClient.ChatEndpoint);
var messages = new List<Message>
{
new Message(Role.System, "You are a helpful assistant designed to output JSON."),
new Message(Role.User, "Who won the world series in 2020?"),
};
var chatRequest = new ChatRequest(messages, "gpt-4-1106-preview", responseFormat: ChatResponseFormat.Json);
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Assert.IsNotEmpty(response.Choices);

foreach (var choice in response.Choices)
{
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice} | Finish Reason: {choice.FinishReason}");
}

response.GetUsage();
}
}
Expand Down
2 changes: 1 addition & 1 deletion OpenAI-DotNet/Chat/ChatResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public IReadOnlyList<Choice> Choices

public override string ToString() => FirstChoice?.ToString() ?? string.Empty;

public static implicit operator string(ChatResponse response) => response.ToString();
public static implicit operator string(ChatResponse response) => response?.ToString();

internal void CopyFrom(ChatResponse other)
{
Expand Down
8 changes: 8 additions & 0 deletions OpenAI-DotNet/Chat/Content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public Content(ContentType type, string input)
}
}

public Content(ImageUrl imageUrl)
{
Type = ContentType.ImageUrl;
ImageUrl = imageUrl;
}

[JsonInclude]
[JsonPropertyName("type")]
[JsonConverter(typeof(JsonStringEnumConverter<ContentType>))]
Expand All @@ -36,5 +42,7 @@ public Content(ContentType type, string input)
[JsonPropertyName("image_url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImageUrl ImageUrl { get; private set; }

public static implicit operator Content(ImageUrl imageUrl) => new Content(imageUrl);
}
}
2 changes: 1 addition & 1 deletion OpenAI-DotNet/Chat/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public IReadOnlyList<Tool> ToolCalls

public override string ToString() => Content?.ToString() ?? string.Empty;

public static implicit operator string(Message message) => message.ToString();
public static implicit operator string(Message message) => message?.ToString();

internal void CopyFrom(Delta other)
{
Expand Down
14 changes: 14 additions & 0 deletions OpenAI-DotNet/Common/ImageDetail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Runtime.Serialization;

namespace OpenAI
{
public enum ImageDetail
{
[EnumMember(Value = "auto")]
Auto,
[EnumMember(Value = "low")]
Low,
[EnumMember(Value = "high")]
High
}
}
11 changes: 10 additions & 1 deletion OpenAI-DotNet/Common/ImageUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ namespace OpenAI
public sealed class ImageUrl
{
[JsonConstructor]
public ImageUrl(string url) => Url = url;
public ImageUrl(string url, ImageDetail detail = ImageDetail.Auto)
{
Url = url;
Detail = detail;
}

[JsonInclude]
[JsonPropertyName("url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Url { get; private set; }

[JsonInclude]
[JsonPropertyName("detail")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImageDetail Detail { get; private set; }
}
}
6 changes: 4 additions & 2 deletions OpenAI-DotNet/OpenAI-DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ More context [on Roger Pincombe's blog](https://rogerpincombe.com/openai-dotnet-
<RepositoryUrl>https://github.com/RageAgainstThePixel/OpenAI-DotNet</RepositoryUrl>
<PackageTags>OpenAI, AI, ML, API, gpt-4, gpt-3.5-tubo, gpt-3, chatGPT, chat-gpt, gpt-2, gpt, dall-e-2, dall-e-3</PackageTags>
<Title>OpenAI API</Title>
<Version>7.3.1</Version>
<PackageReleaseNotes>Version 7.3.1
<Version>7.3.2</Version>
<PackageReleaseNotes>Version 7.3.2
- Added detail parameter to ImageURL
Version 7.3.1
- Fixed json serialization settings when EnableDebug is disabled
Version 7.3.0
- Added AgentsEndpoint
Expand Down
41 changes: 35 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Install-Package OpenAI-DotNet
- [Streaming](#chat-streaming)
- [Tools](#chat-tools) :new:
- [Vision](#chat-vision) :new:
- [Json Mode](#chat-json-mode) :new:
- [Audio](#audio)
- [Create Speech](#create-speech)
- [Create Transcription](#create-transcription)
Expand Down Expand Up @@ -848,7 +849,8 @@ var messages = new List<Message>
};
var chatRequest = new ChatRequest(messages, Model.GPT4);
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content}");
var choice = response.FirstChoice;
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice.Message} | Finish Reason: {choice.FinishReason}");
```

#### [Chat Streaming](https://platform.openai.com/docs/api-reference/chat/create#chat/create-stream)
Expand All @@ -871,7 +873,7 @@ var response = await api.ChatEndpoint.StreamCompletionAsync(chatRequest, partial
}
});
var choice = response.FirstChoice;
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice.Message.Content} | Finish Reason: {choice.FinishReason}");
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice.Message} | Finish Reason: {choice.FinishReason}");
```

Or if using [`IAsyncEnumerable{T}`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1?view=net-5.0) ([C# 8.0+](https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8))
Expand Down Expand Up @@ -943,7 +945,7 @@ var chatRequest = new ChatRequest(messages, tools: tools, toolChoice: "auto");
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Expand All @@ -953,9 +955,9 @@ response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);

messages.Add(response.FirstChoice.Message);

if (!string.IsNullOrEmpty(response.FirstChoice.Message.Content))
if (!string.IsNullOrEmpty(response.FirstChoice))
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishReason}");
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var unitMessage = new Message(Role.User, "celsius");
messages.Add(unitMessage);
Expand Down Expand Up @@ -995,14 +997,41 @@ var messages = new List<Message>
new Message(Role.User, new List<Content>
{
new Content(ContentType.Text, "What's in this image?"),
new Content(ContentType.ImageUrl, "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg")
new ImageUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", ImageDetail.Low)
})
};
var chatRequest = new ChatRequest(messages, model: "gpt-4-vision-preview");
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishDetails}");
```

#### [Chat Json Mode](https://platform.openai.com/docs/guides/text-generation/json-mode)

> :warning: Beta Feature

Important notes:

- When using JSON mode, always instruct the model to produce JSON via some message in the conversation, for example via your system message. If you don't include an explicit instruction to generate JSON, the model may generate an unending stream of whitespace and the request may run continually until it reaches the token limit. To help ensure you don't forget, the API will throw an error if the string "JSON" does not appear somewhere in the context.
- The JSON in the message the model returns may be partial (i.e. cut off) if `finish_reason` is length, which indicates the generation exceeded max_tokens or the conversation exceeded the token limit. To guard against this, check `finish_reason` before parsing the response.
- JSON mode will not guarantee the output matches any specific schema, only that it is valid and parses without errors.

```csharp
var messages = new List<Message>
{
new Message(Role.System, "You are a helpful assistant designed to output JSON."),
new Message(Role.User, "Who won the world series in 2020?"),
};
var chatRequest = new ChatRequest(messages, "gpt-4-1106-preview", responseFormat: ChatResponseFormat.Json);
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);

foreach (var choice in response.Choices)
{
Console.WriteLine($"[{choice.Index}] {choice.Message.Role}: {choice} | Finish Reason: {choice.FinishReason}");
}

response.GetUsage();
```

### [Audio](https://platform.openai.com/docs/api-reference/audio)

Converts audio into text.
Expand Down