From 5fdf44700703c2202d62f9118584ce4522e08216 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 26 Dec 2023 18:32:14 +0100 Subject: [PATCH 01/17] wip net 8 + new api --- DevExtremeAI/DevExtremeAI.csproj | 22 +++- .../OpenAIAPIClient.Completions.cs | 6 + .../OpenAIClient/OpenAIAPIClient.FineTune.cs | 7 +- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 116 ++++++++++++++++++ .../JsonStringArgumentsDictionaryConverter.cs | 2 - .../DevExtremeAIConsoleApp.csproj | 2 +- DevExtremeAILibTest/AIModelTests.cs | 5 +- .../DevExtremeAILibTest.csproj | 19 ++- DevExtremeAIWebApp/DevExtremeAIWebApp.csproj | 9 +- 9 files changed, 172 insertions(+), 16 deletions(-) diff --git a/DevExtremeAI/DevExtremeAI.csproj b/DevExtremeAI/DevExtremeAI.csproj index 300df6e..fa26eea 100644 --- a/DevExtremeAI/DevExtremeAI.csproj +++ b/DevExtremeAI/DevExtremeAI.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net8.0;net7.0;net6.0; enable enable DevExtremeAI is a full and complete implementation of all OpenAI's APIs @@ -12,22 +12,32 @@ https://github.com/AndreaPic/DevExtremeAI True $(VersionPrefix)$(VersionSuffix) - 1.1.0 + 1.8.0 openai;dotnet;aspnet;csharp;gpt-4;gpt-3.5-turbo;davinci;DALL·E;Whisper;fine-tunes - 1.1.0.0 - 1.1.0.0 + 1.8.0.0 + 1.8.0.0 git This library is full compliant to openAI specs and also implement openAI error response. It's very easy to use with asp.net core, has full support to dependency injection and it's very easy to use in libraries without dependency injection. MIT - - (1.1.0 new) *** Full functions implementation *** + - (1.8.0 new) *** .net 8 *** + - (1.1.0) *** Full functions implementation *** - (1.0.4) Added support for multiple stop sequences in completions + + + + + + + + + @@ -38,7 +48,7 @@ - + diff --git a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.Completions.cs b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.Completions.cs index 74b9630..28b3287 100644 --- a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.Completions.cs +++ b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.Completions.cs @@ -9,6 +9,12 @@ namespace DevExtremeAI.OpenAIClient { + /// + /// Create Completions + /// + /// + /// These now are Legacy API + /// partial class OpenAIAPIClient { /// diff --git a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs index de1f6c1..ec7dce7 100644 --- a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs +++ b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs @@ -8,7 +8,12 @@ namespace DevExtremeAI.OpenAIClient { - + /// + /// Manage Fine Tuning API + /// + /// + /// The Fine-tunes endpoint will be shut off on January 4th, 2024. + /// partial class OpenAIAPIClient { /// diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 0ec2b1e..499516f 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -10,6 +10,10 @@ using System.Threading.Tasks; using DevExtremeAI.Utils; using System.Text.Json.Nodes; +using Microsoft.Extensions.Options; +//using static System.Runtime.InteropServices.JavaScript.JSType; +using System.IO; +using System.Reflection; namespace DevExtremeAI.OpenAIDTO { @@ -41,11 +45,13 @@ public void AddMessage(ChatCompletionRequestMessage message) /// /// Function definitions for ai /// + [Obsolete("use tool_choice instead")] private IList? functions; /// /// A list of functions the model may generate JSON inputs for. /// + [Obsolete("use tool_choice instead")] [JsonPropertyName("functions")] public IList? Functions { @@ -67,6 +73,7 @@ public IList? Functions /// Add a function definition to the completion /// /// The funciton definition + [Obsolete("use tool_choice instead")] public void AddFunction(FunctionDefinition functionDefinition) { if (functions == null) @@ -86,6 +93,7 @@ public void AddFunction(FunctionDefinition functionDefinition) /// "none" is the default when no functions are present. /// "auto" is the default if functions are present /// + [Obsolete("use tool_choice instead")] [JsonPropertyName("function_call")] public string? FunctionCall { get; set; } @@ -181,8 +189,116 @@ public void AddStop(string stop) [JsonPropertyName("user")] public string? User { get; set; } + /// + /// Whether to return log probabilities of the output tokens or not. + /// If true, returns the log probabilities of each output token returned in the content of message. + /// This option is currently not available on the gpt-4-vision-preview model. + /// + /// Default False + [JsonPropertyName("logprobs")] + public bool? LogProbs { get; set; } + + /// + /// An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, + /// each with an associated log probability. + /// logprobs must be set to true if this parameter is used. + /// + [JsonPropertyName("top_logprobs")] + public int? TopLogProbs { get; set; } + + + /// + /// An object specifying the format that the model must output.Compatible with gpt-4-1106-preview and gpt-3.5-turbo-1106. + /// Setting to { "type": "json_object" } + /// enables JSON mode, which guarantees the message the model generates is valid JSON. + /// Important: when using JSON mode, you must also instruct the model to produce JSON yourself via a system or user message.Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly "stuck" request.Also note that the message content may be partially cut off if finish_reason= "length", which indicates the generation exceeded max_tokens or the conversation exceeded the max context length. + /// + [JsonPropertyName("response_format")] + public ResponseOutputFormat? ResponseFormat { get; set; } + + /// + /// This feature is in Beta. + /// If specified, our system will make a best effort to sample deterministically, + /// such that repeated requests with the same seed and parameters should return the same result. + /// Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend. + /// + [JsonPropertyName("response_format")] + public int? Seed { get; set; } + + /// + /// A list of tools the model may call. + /// Currently, only functions are supported as a tool. + /// Use this to provide a list of functions the model may generate JSON inputs for. + /// + [JsonPropertyName("tools")] + public List Tools { get; set; } + //TODO: manage hierarchy ^^^ + + /// + /// Ad a tool to Tools List + /// + /// Tool To Add + public void AddTool(ToolDefinition tool) + { + if (Tools == null) + { + Tools = new List(); + } + Tools.Add(tool); + } + + /// + /// + /// + [JsonPropertyName("tools")] + public object? ToolChoice { get; set; } + //TODO: not object? but new baseclass + //TODO: continue form here https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice + + } + + public abstract class ToolDefinition + { + [JsonPropertyName("type")] + public string Type { get; protected set; } + } + + public class FunctionTool : ToolDefinition + { + public FunctionTool() + { + Type = "function"; + } + + [JsonPropertyName("function")] + public FunctionDefinition Function { get; set; } + } + + public abstract class ResponseOutputFormat + { + [JsonPropertyName("type")] + public string Type { get; protected set; } + } + + public class ResponseOutputTextFormat : ResponseOutputFormat + { + public ResponseOutputTextFormat() + { + Type = "text"; + } + } + + public class ResponseOutputJSONFormat : ResponseOutputFormat + { + public ResponseOutputJSONFormat() + { + Type = "json_object"; + } + } + + public class ChatCompletionRequestMessage { /// diff --git a/DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs b/DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs index 2852914..937cf27 100644 --- a/DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs +++ b/DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs @@ -21,8 +21,6 @@ internal sealed class JsonStringArgumentsDictionaryConverter throw new JsonException(); } - var dictionary = new Dictionary(); - var arguments = reader.GetString(); if (!string.IsNullOrEmpty(arguments)) diff --git a/DevExtremeAIConsoleApp/DevExtremeAIConsoleApp.csproj b/DevExtremeAIConsoleApp/DevExtremeAIConsoleApp.csproj index d0c4543..019c3aa 100644 --- a/DevExtremeAIConsoleApp/DevExtremeAIConsoleApp.csproj +++ b/DevExtremeAIConsoleApp/DevExtremeAIConsoleApp.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0; + net8.0;net7.0;net6.0; Exe enable enable diff --git a/DevExtremeAILibTest/AIModelTests.cs b/DevExtremeAILibTest/AIModelTests.cs index 1ee2f56..c0c6075 100644 --- a/DevExtremeAILibTest/AIModelTests.cs +++ b/DevExtremeAILibTest/AIModelTests.cs @@ -21,10 +21,11 @@ public async Task GetModelTest() var models = await openAiapiClient.GetModelsAsync(); Assert.NotNull(models?.OpenAIResponse?.Data); Assert.True(models?.OpenAIResponse.Data.Count > 0); - var model = await openAiapiClient.GetModelAsync(models.OpenAIResponse.Data[0].Id); + int modelIndex = 0; + var model = await openAiapiClient.GetModelAsync(models.OpenAIResponse.Data[modelIndex].Id); Assert.NotNull(model?.OpenAIResponse); Assert.NotNull(model?.OpenAIResponse?.Permissions); - Assert.True(model.OpenAIResponse.Permissions.Count > 0); + Assert.True(model.OpenAIResponse.Permissions.Count == 0); } } } diff --git a/DevExtremeAILibTest/DevExtremeAILibTest.csproj b/DevExtremeAILibTest/DevExtremeAILibTest.csproj index 04da20c..22635a3 100644 --- a/DevExtremeAILibTest/DevExtremeAILibTest.csproj +++ b/DevExtremeAILibTest/DevExtremeAILibTest.csproj @@ -1,7 +1,7 @@  - net7.0;net6.0; + net8.0;net7.0;net6.0; enable enable false @@ -17,8 +17,21 @@ Always - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/DevExtremeAIWebApp/DevExtremeAIWebApp.csproj b/DevExtremeAIWebApp/DevExtremeAIWebApp.csproj index 0fc3402..cad4020 100644 --- a/DevExtremeAIWebApp/DevExtremeAIWebApp.csproj +++ b/DevExtremeAIWebApp/DevExtremeAIWebApp.csproj @@ -1,12 +1,19 @@  - net7.0;net6.0; + net8.0;net7.0;net6.0; enable enable 9ed09234-57c0-4f28-9a3e-d8c589ac44f8 Linux + + + + + + + From 47ff236739f662d044bd1b6d933ce70dfc724bfa Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Wed, 27 Dec 2023 23:40:27 +0100 Subject: [PATCH 02/17] Updated Chat completion DTO\API --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 323 +++++++++++++++++-- DevExtremeAI/OpenAIDTO/FunctionDefinition.cs | 18 +- 2 files changed, 318 insertions(+), 23 deletions(-) diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 499516f..585cb2e 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -11,9 +11,13 @@ using DevExtremeAI.Utils; using System.Text.Json.Nodes; using Microsoft.Extensions.Options; -//using static System.Runtime.InteropServices.JavaScript.JSType; using System.IO; using System.Reflection; +#if NET7_0_OR_GREATER +using static System.Runtime.InteropServices.JavaScript.JSType; +#endif +using System.Threading.Channels; +using System.Net.Mime; namespace DevExtremeAI.OpenAIDTO { @@ -31,7 +35,10 @@ public class CreateChatCompletionRequest /// The messages to generate chat completions for, in the [chat format](/docs/guides/chat/introduction). /// [JsonPropertyName("messages")] - public List Messages { get; private set; } = new List(); + public List Messages { get; private set; } = new List(); + //TODO: manage hierarchy ^^^ + //TODO: add specialized AddMessage + /// /// Add a message to the completion @@ -247,20 +254,44 @@ public void AddTool(ToolDefinition tool) Tools.Add(tool); } + /// - /// + /// Controls which (if any) function is called by the model. none means the model will not call a function and instead generates a message. auto means the model can pick between generating a message or calling a function. Specifying a particular function via {"type": "function", "function": {"name": "my_function"}} forces the model to call that function. + /// none is the default when no functions are present.auto is the default if functions are present. /// - [JsonPropertyName("tools")] - public object? ToolChoice { get; set; } - //TODO: not object? but new baseclass - //TODO: continue form here https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice + [JsonPropertyName("tool_choice")] + public object? ToolChoice { get; private set;} + + /// + /// auto is the default if functions are present. + /// + public void SetAutoToolChoice() + { + ToolChoice = "auto"; + } + /// + /// none is the default when no functions are present. + /// + public void SetNoneToolChoice() + { + ToolChoice = "none"; + } + + public void SetFunctionNameToolChoice(FunctionNameTool functionNameTool) + { + ToolChoice = functionNameTool; + } } + //TODO: continue form here https://platform.openai.com/docs/api-reference/chat/streaming public abstract class ToolDefinition { + /// + /// The type of the tool. Currently, only function is supported. + /// [JsonPropertyName("type")] public string Type { get; protected set; } } @@ -276,6 +307,18 @@ public FunctionTool() public FunctionDefinition Function { get; set; } } + public class FunctionNameTool : ToolDefinition + { + public FunctionNameTool() + { + Type = "function"; + } + + [JsonPropertyName("function")] + public FunctionNameDefinition Function { get; set; } + } + + public abstract class ResponseOutputFormat { [JsonPropertyName("type")] @@ -299,34 +342,238 @@ public ResponseOutputJSONFormat() } - public class ChatCompletionRequestMessage + public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage { /// /// The role of the author of this message. One of system, user, or assistant. /// [JsonPropertyName("role")] - - public ChatCompletionMessageRoleEnum Role { get; set; } + new public ChatCompletionMessageRoleEnum Role + { + get + { + return base.Role; + } + set + { + base.Role = value; + } + } /// - /// The contents of the message. + /// The name and arguments of a function that should be called, as generated by the model. /// - [JsonPropertyName("content")] - public string Content { get; set; } + [Obsolete("Instead use ToolCalls")] + [JsonPropertyName("function_call")] + public FunctionCallDefinition? FunctionCall { get; set; } + + } + + //TODO: review as https://platform.openai.com/docs/api-reference/chat/create + public abstract class ChatCompletionRoleRequestMessage + { /// + /// The role of the author of this message. One of system, user, or assistant. + /// + [JsonPropertyName("role")] + public ChatCompletionMessageRoleEnum Role { get; internal protected set; } + } + + public abstract class ChatCompletionNameRequestMessage : ChatCompletionRoleRequestMessage + { + /// /// The name of the user in a multi-user chat /// The name of the author of this message. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of 64 characters. /// [JsonPropertyName("name")] public string? Name { get; set; } + } + public abstract class ChatCompletionStringRequestMessage : ChatCompletionNameRequestMessage + { + /// + /// The contents of the message. + /// + [JsonPropertyName("content")] + public string Content { get; set; } + } + + /// + /// System Message + /// + public class ChatCompletionSystemMessage : ChatCompletionStringRequestMessage + { + + public ChatCompletionSystemMessage() + { + Role = ChatCompletionMessageRoleEnum.System; + } + } + + /// + /// User Message with text contents of the message + /// + public class ChatCompletionUserContentMessage : ChatCompletionStringRequestMessage + { + public ChatCompletionUserContentMessage() + { + Role = ChatCompletionMessageRoleEnum.User; + } + } + + /// + /// User Message with Array of content parts + /// + public class ChatCompletionUserContentsMessage : ChatCompletionNameRequestMessage + { + public ChatCompletionUserContentsMessage() + { + Role = ChatCompletionMessageRoleEnum.User; + } /// - /// The name and arguments of a function that should be called, as generated by the model. + /// An array of content parts with a defined type, each can be of type text or image_url when passing in images. + /// You can pass multiple images by adding multiple image_url content parts. + /// Image input is only supported when using the gpt-4-visual-preview model. /// - [JsonPropertyName("function_call")] - public FunctionCallDefinition? FunctionCall { get; set; } + [JsonPropertyName("content")] + public List Contents { get; private set; } = new List(); + } + + /// + /// Assistant message + /// + public class ChatCompletionAssistantMessage : ChatCompletionStringRequestMessage + { + public ChatCompletionAssistantMessage() + { + Role = ChatCompletionMessageRoleEnum.Assistant; + } + + /// + /// The tool calls generated by the model, such as function calls. + /// + [JsonPropertyName("tool_calls")] + public List ToolCalls { get; private set; } = new List(); + } + + /// + /// Tool message + /// + public class ChatCompletionToolMessage : ChatCompletionRoleRequestMessage + { + public ChatCompletionToolMessage() + { + Role = ChatCompletionMessageRoleEnum.Tool; + } + + /// + /// The contents of the tool message. + /// + [JsonPropertyName("content")] + public string Content { get; set; } + + /// + /// Tool call that this message is responding to. + /// + [JsonPropertyName("tool_call_id")] + public string ToolCallId { get; set; } + } + + + public class ToolCall + { + /// + /// The ID of the tool call. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// The type of the tool. Currently, only function is supported. + /// + [JsonPropertyName("type")] + public string Type { get => "function"; /*set;*/ } + + /// + /// The function that the model called. + /// + [JsonPropertyName("function")] + public FunctionDefinition FunctionCall { get; set; } + } + + + /// + /// Base class for content items used in Contents of ChatCompletionUserContentsMessage + /// + public abstract class ContentItem + { + /// + /// The type of the content part. + /// + [JsonPropertyName("type")] + public ContentItemTypes ContentType { get; internal protected set; } + } + + public class ContentTextItem : ContentItem + { + public ContentTextItem() + { + ContentType = ContentItemTypes.Text; + } + + /// + /// The text content. + /// + [JsonPropertyName("text")] + public string Text { get; set; } + } + + public class ContentImageItem : ContentItem + { + public ContentImageItem() + { + ContentType = ContentItemTypes.ImageUrl; + } + + [JsonPropertyName("image_url")] + public ImageUrl ImageUrl { get; private set; } = new ImageUrl(); + } + + public class ImageUrl + { + /// + /// Either a URL of the image or the base64 encoded image data. + /// + [JsonPropertyName("url")] + public string ImageURl { get; set; } + /// + /// Specifies the detail level of the image. + /// + [JsonPropertyName("detail")] + public ImageDetailLevel? Detail { get; set; } + } + + + [JsonConverter(typeof(JsonStringEnumConverterEx))] + public enum ContentItemTypes + { + [EnumMember(Value = "Text")] + Text = 0, + [EnumMember(Value = "image_url")] + ImageUrl = 1, + } + + [JsonConverter(typeof(JsonStringEnumConverterEx))] + public enum ImageDetailLevel + { + [EnumMember(Value = "auto")] + Auto = 0, + [EnumMember(Value = "low")] + Low = 1, + [EnumMember(Value = "high")] + High = 2, } @@ -365,44 +612,80 @@ public enum ChatCompletionMessageRoleEnum [EnumMember(Value = "assistant")] Assistant = 2, [EnumMember(Value = "function")] - Function = 3 + Function = 3, + [EnumMember(Value = "tool")] + Tool = 4 } - + /// + /// Represents a chat completion response returned by model, based on the provided input. + /// public class CreateChatCompletionResponse { + /// + /// A unique identifier for the chat completion. + /// [JsonPropertyName("id")] public string ID { get; set; } + /// + /// The object type, which is always chat.completion. + /// [JsonPropertyName("object")] public string Object { get; set; } + /// + /// The Unix timestamp (in seconds) of when the chat completion was created. + /// [JsonPropertyName("created")] public double Created { get; set; } + /// + /// The model used for the chat completion. + /// [JsonPropertyName("model")] public string Model { get; set; } + /// + /// A list of chat completion choices. Can be more than one if n is greater than 1. + /// [JsonPropertyName("choices")] public List Choices { get; set; } + /// + /// Usage statistics for the completion request. + /// [JsonPropertyName("usage")] public CreateCompletionResponseUsage? Usage { get; set; } + + /// + /// This fingerprint represents the backend configuration that the model runs with. + /// Can be used in conjunction with the seed request parameter to understand when backend changes have been made that might impact determinism. + /// + [JsonPropertyName("system_fingerprint")] + public string SystemFingerprint { get; set; } } public class CreateChatCompletionResponseChoicesInner { - + /// + /// The index of the choice in the list of choices. + /// [JsonPropertyName("index")] public double? Index { get; set; } + /// + /// A chat completion message generated by the model. + /// [JsonPropertyName("message")] public ChatCompletionResponseMessage? Message { get; set; } [JsonPropertyName("delta")] public ChatCompletionResponseMessage? Delta { get; set; } - + /// + /// The reason the model stopped generating tokens. This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified in the request was reached, content_filter if content was omitted due to a flag from our content filters, tool_calls if the model called a tool, or function_call (deprecated) if the model called a function. + /// [JsonPropertyName("finish_reason")] public string? FinishReason { get; set; } } diff --git a/DevExtremeAI/OpenAIDTO/FunctionDefinition.cs b/DevExtremeAI/OpenAIDTO/FunctionDefinition.cs index bebed53..8e0ddfd 100644 --- a/DevExtremeAI/OpenAIDTO/FunctionDefinition.cs +++ b/DevExtremeAI/OpenAIDTO/FunctionDefinition.cs @@ -11,16 +11,24 @@ namespace DevExtremeAI.OpenAIDTO /// - /// This object define a schema for the function's description used by OpenAI + /// This object define a function's name (you can use it to specify a function to call) /// - public class FunctionDefinition + public class FunctionNameDefinition { /// - /// The name of the funciont + /// The name of the function /// [JsonPropertyName("name")] public string Name { get; set; } + } + + + /// + /// This object define a schema for the function's description used by OpenAI + /// + public class FunctionDefinition : FunctionNameDefinition + { /// /// The description of the function behavior /// @@ -132,4 +140,8 @@ public class FunctionCallDefinition [JsonConverter(typeof(JsonStringArgumentsDictionaryConverter))] public IDictionary Arguments { get; set; } = new Dictionary(); } + + + + } From 075220a93421f9ddd3b38e10f95c60e945c2f386 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Sun, 31 Dec 2023 16:19:23 +0100 Subject: [PATCH 03/17] wip --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 39 ++++-- DevExtremeAI/Utils/MessageJsonConverter.cs | 124 ++++++++++++++++++ .../CustomMessageSerializerTests.cs | 34 +++++ 3 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 DevExtremeAI/Utils/MessageJsonConverter.cs create mode 100644 DevExtremeAILibTest/CustomMessageSerializerTests.cs diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 585cb2e..6693acf 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -49,6 +49,17 @@ public void AddMessage(ChatCompletionRequestMessage message) Messages.Add(message); } + /// + /// Add a message to the completion + /// + /// The message to add + public void AddMessage(ChatCompletionRoleRequestMessage message) + { + Messages.Add(message); + } + + + /// /// Function definitions for ai /// @@ -60,11 +71,11 @@ public void AddMessage(ChatCompletionRequestMessage message) /// [Obsolete("use tool_choice instead")] [JsonPropertyName("functions")] - public IList? Functions - { + public IList? Functions + { get { - if ( (functions == null) || (functions.Count == 0)) + if ((functions == null) || (functions.Count == 0)) { return null; } @@ -230,7 +241,7 @@ public void AddStop(string stop) /// Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend. /// [JsonPropertyName("response_format")] - public int? Seed { get; set; } + public int? Seed { get; set; } /// /// A list of tools the model may call. @@ -260,7 +271,7 @@ public void AddTool(ToolDefinition tool) /// none is the default when no functions are present.auto is the default if functions are present. /// [JsonPropertyName("tool_choice")] - public object? ToolChoice { get; private set;} + public object? ToolChoice { get; private set; } /// /// auto is the default if functions are present. @@ -348,8 +359,8 @@ public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage /// The role of the author of this message. One of system, user, or assistant. /// [JsonPropertyName("role")] - new public ChatCompletionMessageRoleEnum Role - { + new public ChatCompletionMessageRoleEnum Role + { get { return base.Role; @@ -371,7 +382,8 @@ public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage //TODO: review as https://platform.openai.com/docs/api-reference/chat/create - public abstract class ChatCompletionRoleRequestMessage + [System.Text.Json.Serialization.JsonConverterAttribute(typeof(MessageJsonConverter))] + public abstract class ChatCompletionRoleRequestMessage { /// /// The role of the author of this message. One of system, user, or assistant. @@ -453,7 +465,7 @@ public ChatCompletionAssistantMessage() /// /// The tool calls generated by the model, such as function calls. /// - [JsonPropertyName("tool_calls")] + [JsonPropertyName("tool_calls")] public List ToolCalls { get; private set; } = new List(); } @@ -481,6 +493,15 @@ public ChatCompletionToolMessage() } + [Obsolete("use tools instead")] + public class ChatCompletionFunctionMessage : ChatCompletionStringRequestMessage + { + public ChatCompletionFunctionMessage() + { + Role = ChatCompletionMessageRoleEnum.Function; + } + } + public class ToolCall { /// diff --git a/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs new file mode 100644 index 0000000..e80f66d --- /dev/null +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -0,0 +1,124 @@ +using DevExtremeAI.OpenAIDTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace DevExtremeAI.Utils +{ + public class MessageJsonConverter : JsonConverter + { + + //TODO: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?source=recommendations&pivots=dotnet-8-0#support-polymorphic-deserialization + + + public override bool CanConvert(Type typeToConvert) => + typeof(ChatCompletionRoleRequestMessage).IsAssignableFrom(typeToConvert); + + public override ChatCompletionRoleRequestMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ChatCompletionRoleRequestMessage? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader specializedReaderClone = reader; + try + { + Utf8JsonReader readerClone = reader; + var baseMessage = JsonSerializer.Deserialize(ref readerClone); + if (baseMessage != null) + { + switch (baseMessage.Role) + { + case ChatCompletionMessageRoleEnum.Assistant: + { + ret = JsonSerializer.Deserialize(ref specializedReaderClone); + } + break; + case ChatCompletionMessageRoleEnum.Function: + { + ret = JsonSerializer.Deserialize(ref specializedReaderClone); + + } + break; + case ChatCompletionMessageRoleEnum.System: + { + ret = JsonSerializer.Deserialize(ref specializedReaderClone); + } + break; + case ChatCompletionMessageRoleEnum.Tool: + { + ret = JsonSerializer.Deserialize(ref specializedReaderClone); + } + break; + case ChatCompletionMessageRoleEnum.User: + { + ret = DeserializeUserRequestMessage(ref specializedReaderClone, typeToConvert, options); + } + break; + } + } + + throw new JsonException(); + } + finally + { + reader = originalReader; + } + + } + + private ChatCompletionRoleRequestMessage? DeserializeUserRequestMessage(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + Utf8JsonReader originalReader = reader; + Utf8JsonReader cloneReader = reader; + ChatCompletionRoleRequestMessage? ret = null; + try + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + cloneReader.Read(); + + while (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.EndObject) + { + return ret; + } + + if (cloneReader.TokenType == JsonTokenType.PropertyName) + { + string? propertyName = cloneReader.GetString(); + if (propertyName == nameof(ChatCompletionUserContentMessage.Content)) + { + if (cloneReader.TokenType == JsonTokenType.String) + { + ret = JsonSerializer.Deserialize(ref cloneReader); + } + else + { + ret = JsonSerializer.Deserialize(ref cloneReader); + } + } + } + } + } + finally + { + reader = originalReader; + } + + return ret; + + } + + public override void Write(Utf8JsonWriter writer, ChatCompletionRoleRequestMessage value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } + } + +} diff --git a/DevExtremeAILibTest/CustomMessageSerializerTests.cs b/DevExtremeAILibTest/CustomMessageSerializerTests.cs new file mode 100644 index 0000000..b44a807 --- /dev/null +++ b/DevExtremeAILibTest/CustomMessageSerializerTests.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using DevExtremeAI.OpenAIDTO; +using DevExtremeAI.OpenAIClient; + +namespace DevExtremeAILibTest +{ + public class CustomMessageSerializerTests : IClassFixture + { + private readonly TestApplication _factory; + + public CustomMessageSerializerTests(TestApplication factory) + { + _factory = factory; + } + + [Fact] + public async Task RoundTripMessageSerializationTest() + { + using (var scope = _factory.Services.CreateScope()) + { + var openAiapiClient = scope.ServiceProvider.GetService(); + CreateChatCompletionRequest request = new CreateChatCompletionRequest(); + request.AddMessage(new ChatCompletionFunctionMessage()); + + + //var response = await openAiapiClient.CreateTranscriptionsAsync(request); + //Assert.NotNull(response); + //Assert.False(response.HasError,response?.ErrorResponse?.Error?.Message); + //Assert.NotNull(response?.OpenAIResponse?.Text); + //Assert.True(response.OpenAIResponse.Text.Contains("prova")); + } + } + } +} \ No newline at end of file From 58d708edb3692fdedf3f0f94c39319e7927f206b Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Sun, 31 Dec 2023 23:49:32 +0100 Subject: [PATCH 04/17] wip error reader clone --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 23 +++- DevExtremeAI/Utils/MessageJsonConverter.cs | 126 ++++++++++++++++-- .../Utils/MessageListJsonConverter.cs | 85 ++++++++++++ .../CustomMessageSerializerTests.cs | 24 +++- 4 files changed, 237 insertions(+), 21 deletions(-) create mode 100644 DevExtremeAI/Utils/MessageListJsonConverter.cs diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 6693acf..ab0bb11 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -18,6 +18,7 @@ #endif using System.Threading.Channels; using System.Net.Mime; +using System.Text.Json; namespace DevExtremeAI.OpenAIDTO { @@ -35,7 +36,8 @@ public class CreateChatCompletionRequest /// The messages to generate chat completions for, in the [chat format](/docs/guides/chat/introduction). /// [JsonPropertyName("messages")] - public List Messages { get; private set; } = new List(); + [JsonConverter(typeof(MessageListJsonConverter))] + public List Messages { get; set; } = new List(); //TODO: manage hierarchy ^^^ //TODO: add specialized AddMessage @@ -46,6 +48,10 @@ public class CreateChatCompletionRequest /// The message to add public void AddMessage(ChatCompletionRequestMessage message) { + if (Messages == null) + { + Messages = new List(); + } Messages.Add(message); } @@ -55,6 +61,10 @@ public void AddMessage(ChatCompletionRequestMessage message) /// The message to add public void AddMessage(ChatCompletionRoleRequestMessage message) { + if (Messages == null) + { + Messages = new List(); + } Messages.Add(message); } @@ -240,7 +250,7 @@ public void AddStop(string stop) /// such that repeated requests with the same seed and parameters should return the same result. /// Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend. /// - [JsonPropertyName("response_format")] + [JsonPropertyName("seed")] public int? Seed { get; set; } /// @@ -382,8 +392,9 @@ public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage //TODO: review as https://platform.openai.com/docs/api-reference/chat/create + //TODO: stack overflow access violation https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-6-0#required-properties [System.Text.Json.Serialization.JsonConverterAttribute(typeof(MessageJsonConverter))] - public abstract class ChatCompletionRoleRequestMessage + public class ChatCompletionRoleRequestMessage { /// /// The role of the author of this message. One of system, user, or assistant. @@ -392,7 +403,7 @@ public abstract class ChatCompletionRoleRequestMessage public ChatCompletionMessageRoleEnum Role { get; internal protected set; } } - public abstract class ChatCompletionNameRequestMessage : ChatCompletionRoleRequestMessage + public class ChatCompletionNameRequestMessage : ChatCompletionRoleRequestMessage { /// /// The name of the user in a multi-user chat @@ -401,7 +412,7 @@ public abstract class ChatCompletionNameRequestMessage : ChatCompletionRoleReque [JsonPropertyName("name")] public string? Name { get; set; } } - public abstract class ChatCompletionStringRequestMessage : ChatCompletionNameRequestMessage + public class ChatCompletionStringRequestMessage : ChatCompletionNameRequestMessage { /// /// The contents of the message. @@ -580,7 +591,7 @@ public class ImageUrl [JsonConverter(typeof(JsonStringEnumConverterEx))] public enum ContentItemTypes { - [EnumMember(Value = "Text")] + [EnumMember(Value = "text")] Text = 0, [EnumMember(Value = "image_url")] ImageUrl = 1, diff --git a/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs index e80f66d..912cf3d 100644 --- a/DevExtremeAI/Utils/MessageJsonConverter.cs +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -14,6 +14,15 @@ public class MessageJsonConverter : JsonConverter typeof(ChatCompletionRoleRequestMessage).IsAssignableFrom(typeToConvert); @@ -22,50 +31,104 @@ public override bool CanConvert(Type typeToConvert) => { ChatCompletionRoleRequestMessage? ret = null; Utf8JsonReader originalReader = reader; - Utf8JsonReader specializedReaderClone = reader; + Utf8JsonReader specializedReader = reader; try { Utf8JsonReader readerClone = reader; - var baseMessage = JsonSerializer.Deserialize(ref readerClone); - if (baseMessage != null) + var role = FindRole(ref readerClone, typeToConvert, options); + if (role != null) { - switch (baseMessage.Role) + switch (role) { case ChatCompletionMessageRoleEnum.Assistant: { - ret = JsonSerializer.Deserialize(ref specializedReaderClone); + ret = JsonSerializer.Deserialize(ref specializedReader); } break; case ChatCompletionMessageRoleEnum.Function: { - ret = JsonSerializer.Deserialize(ref specializedReaderClone); + ret = JsonSerializer.Deserialize(ref specializedReader); } break; case ChatCompletionMessageRoleEnum.System: { - ret = JsonSerializer.Deserialize(ref specializedReaderClone); + ret = JsonSerializer.Deserialize(ref specializedReader); } break; case ChatCompletionMessageRoleEnum.Tool: { - ret = JsonSerializer.Deserialize(ref specializedReaderClone); + ret = JsonSerializer.Deserialize(ref specializedReader); } break; case ChatCompletionMessageRoleEnum.User: { - ret = DeserializeUserRequestMessage(ref specializedReaderClone, typeToConvert, options); + ret = DeserializeUserRequestMessage(ref specializedReader, typeToConvert, options); } break; + default: + { + throw new JsonException(); + } } + return ret; } - throw new JsonException(); } finally { reader = originalReader; } + } + + private ChatCompletionMessageRoleEnum? FindRole(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ChatCompletionMessageRoleEnum? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader cloneReader = reader; + + try + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + while (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.EndObject) + { + return ret; + } + + if (cloneReader.TokenType == JsonTokenType.PropertyName) + { + string? propertyName = cloneReader.GetString(); + if (propertyName == nameof(ChatCompletionRoleRequestMessage.Role).ToLower()) + { + if (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.String) + { + var stringValue = cloneReader.GetString(); + if (!String.IsNullOrEmpty(stringValue)) + { + ret = (ChatCompletionMessageRoleEnum)Enum.Parse(typeof(ChatCompletionMessageRoleEnum), stringValue,true); + return ret; + } + } + } + + } + } + } + + } + finally + { + reader = originalReader; + } + return ret; } @@ -73,6 +136,7 @@ public override bool CanConvert(Type typeToConvert) => { Utf8JsonReader originalReader = reader; Utf8JsonReader cloneReader = reader; + Utf8JsonReader specializedReader = reader; ChatCompletionRoleRequestMessage? ret = null; try { @@ -86,22 +150,24 @@ public override bool CanConvert(Type typeToConvert) => { if (cloneReader.TokenType == JsonTokenType.EndObject) { + ret = JsonSerializer.Deserialize(ref specializedReader); return ret; } if (cloneReader.TokenType == JsonTokenType.PropertyName) { string? propertyName = cloneReader.GetString(); - if (propertyName == nameof(ChatCompletionUserContentMessage.Content)) + if (propertyName == nameof(ChatCompletionUserContentMessage.Content).ToLower()) { if (cloneReader.TokenType == JsonTokenType.String) { - ret = JsonSerializer.Deserialize(ref cloneReader); + ret = JsonSerializer.Deserialize(ref specializedReader); } else { - ret = JsonSerializer.Deserialize(ref cloneReader); + ret = JsonSerializer.Deserialize(ref specializedReader); } + break; } } } @@ -117,7 +183,39 @@ public override bool CanConvert(Type typeToConvert) => public override void Write(Utf8JsonWriter writer, ChatCompletionRoleRequestMessage value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, options); + switch (value) + { + case ChatCompletionAssistantMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ChatCompletionFunctionMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ChatCompletionSystemMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ChatCompletionToolMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ChatCompletionUserContentMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ChatCompletionUserContentsMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + } } } diff --git a/DevExtremeAI/Utils/MessageListJsonConverter.cs b/DevExtremeAI/Utils/MessageListJsonConverter.cs new file mode 100644 index 0000000..7a183ab --- /dev/null +++ b/DevExtremeAI/Utils/MessageListJsonConverter.cs @@ -0,0 +1,85 @@ +using DevExtremeAI.OpenAIDTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace DevExtremeAI.Utils +{ + public class MessageListJsonConverter : JsonConverter> + { + + //TODO: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?source=recommendations&pivots=dotnet-8-0#support-polymorphic-deserialization + + //public override ChatCompletionRoleRequestMessage ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + //{ + // return base.ReadAsPropertyName(ref reader, typeToConvert, options); + //} + + //public override bool Equals(object? obj) + //{ + // return base.Equals(obj); + //} + + public override bool CanConvert(Type typeToConvert) => true; + //typeof(List).IsAssignableFrom(typeToConvert); + + public override List Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader specializedReader = reader; + try + { + Utf8JsonReader readerClone = reader; + ret = new List(); + + while (readerClone.Read()) + { + + if (readerClone.TokenType == JsonTokenType.EndArray) + { + break; + } + + if (readerClone.TokenType == JsonTokenType.StartObject) + { + var item = JsonSerializer.Deserialize(ref readerClone); + if (item != null) + { + ret.Add(item); + } + } + } + + return ret; + + } + finally + { + reader = originalReader; + } + + } + + public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + if (value != null) + { + foreach (ChatCompletionRoleRequestMessage item in value) + { + JsonSerializer.Serialize(writer, item, options); + } + } + + writer.WriteEndArray(); + + } + } + +} diff --git a/DevExtremeAILibTest/CustomMessageSerializerTests.cs b/DevExtremeAILibTest/CustomMessageSerializerTests.cs index b44a807..ace29eb 100644 --- a/DevExtremeAILibTest/CustomMessageSerializerTests.cs +++ b/DevExtremeAILibTest/CustomMessageSerializerTests.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using DevExtremeAI.OpenAIDTO; using DevExtremeAI.OpenAIClient; +using System.Text.Json; namespace DevExtremeAILibTest { @@ -14,13 +15,34 @@ public CustomMessageSerializerTests(TestApplication factory) } [Fact] - public async Task RoundTripMessageSerializationTest() + public void RoundTripMessageSerializationTest() { using (var scope = _factory.Services.CreateScope()) { var openAiapiClient = scope.ServiceProvider.GetService(); CreateChatCompletionRequest request = new CreateChatCompletionRequest(); request.AddMessage(new ChatCompletionFunctionMessage()); + request.AddMessage(new ChatCompletionSystemMessage()); + request.AddMessage(new ChatCompletionAssistantMessage()); + request.AddMessage(new ChatCompletionToolMessage()); + request.AddMessage(new ChatCompletionUserContentMessage()); + request.AddMessage(new ChatCompletionUserContentsMessage()); + + var json = JsonSerializer.Serialize(request); + + Assert.NotNull(json); + + var deserializedRequest = JsonSerializer.Deserialize(json); + Assert.NotNull(deserializedRequest); + Assert.IsType(deserializedRequest); + Assert.True(deserializedRequest.Messages.Count == 6); + Assert.IsType(deserializedRequest.Messages[0]); + Assert.IsType(deserializedRequest.Messages[1]); + Assert.IsType(deserializedRequest.Messages[2]); + Assert.IsType(deserializedRequest.Messages[3]); + Assert.IsType(deserializedRequest.Messages[4]); + Assert.IsType(deserializedRequest.Messages[5]); + //var response = await openAiapiClient.CreateTranscriptionsAsync(request); From 2fe88103257a4a02704a8be4c6c5421d206dba76 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Mon, 1 Jan 2024 01:02:49 +0100 Subject: [PATCH 05/17] yeahhh test ok, happy 2024! --- DevExtremeAI/Utils/MessageJsonConverter.cs | 42 +++++++++---------- .../Utils/MessageListJsonConverter.cs | 20 +++------ 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs index 912cf3d..6cb913f 100644 --- a/DevExtremeAI/Utils/MessageJsonConverter.cs +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -14,16 +14,6 @@ public class MessageJsonConverter : JsonConverter typeof(ChatCompletionRoleRequestMessage).IsAssignableFrom(typeToConvert); @@ -42,28 +32,28 @@ public override bool CanConvert(Type typeToConvert) => { case ChatCompletionMessageRoleEnum.Assistant: { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); } break; case ChatCompletionMessageRoleEnum.Function: { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); } break; case ChatCompletionMessageRoleEnum.System: { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); } break; case ChatCompletionMessageRoleEnum.Tool: { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); } break; case ChatCompletionMessageRoleEnum.User: { - ret = DeserializeUserRequestMessage(ref specializedReader, typeToConvert, options); + ret = DeserializeUserRequestMessage(ref reader, typeToConvert, options); } break; default: @@ -77,7 +67,7 @@ public override bool CanConvert(Type typeToConvert) => } finally { - reader = originalReader; + //reader = originalReader; } } @@ -144,13 +134,15 @@ public override bool CanConvert(Type typeToConvert) => { throw new JsonException(); } - cloneReader.Read(); while (cloneReader.Read()) { if (cloneReader.TokenType == JsonTokenType.EndObject) { - ret = JsonSerializer.Deserialize(ref specializedReader); + if (ret == null) + { + ret = JsonSerializer.Deserialize(ref reader); + } return ret; } @@ -159,22 +151,26 @@ public override bool CanConvert(Type typeToConvert) => string? propertyName = cloneReader.GetString(); if (propertyName == nameof(ChatCompletionUserContentMessage.Content).ToLower()) { + cloneReader.Read(); if (cloneReader.TokenType == JsonTokenType.String) { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); } - else + else if (cloneReader.TokenType == JsonTokenType.StartArray) { - ret = JsonSerializer.Deserialize(ref specializedReader); + ret = JsonSerializer.Deserialize(ref reader); + } + if (ret != null) + { + break; } - break; } } } } finally { - reader = originalReader; + //reader = originalReader; } return ret; diff --git a/DevExtremeAI/Utils/MessageListJsonConverter.cs b/DevExtremeAI/Utils/MessageListJsonConverter.cs index 7a183ab..11b1398 100644 --- a/DevExtremeAI/Utils/MessageListJsonConverter.cs +++ b/DevExtremeAI/Utils/MessageListJsonConverter.cs @@ -14,16 +14,6 @@ public class MessageListJsonConverter : JsonConverter true; //typeof(List).IsAssignableFrom(typeToConvert); @@ -37,17 +27,17 @@ public override List Read(ref Utf8JsonReader r Utf8JsonReader readerClone = reader; ret = new List(); - while (readerClone.Read()) + while (reader.Read()) { - if (readerClone.TokenType == JsonTokenType.EndArray) + if (reader.TokenType == JsonTokenType.EndArray) { break; } - if (readerClone.TokenType == JsonTokenType.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - var item = JsonSerializer.Deserialize(ref readerClone); + var item = JsonSerializer.Deserialize(ref reader); if (item != null) { ret.Add(item); @@ -60,7 +50,7 @@ public override List Read(ref Utf8JsonReader r } finally { - reader = originalReader; + //reader = originalReader; } } From 2e70de4366b37f80b9baa5df28cbbc3dac8b821a Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Mon, 1 Jan 2024 11:28:36 +0100 Subject: [PATCH 06/17] test in progress --- .../CustomMessageSerializerTests.cs | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/DevExtremeAILibTest/CustomMessageSerializerTests.cs b/DevExtremeAILibTest/CustomMessageSerializerTests.cs index ace29eb..79b492c 100644 --- a/DevExtremeAILibTest/CustomMessageSerializerTests.cs +++ b/DevExtremeAILibTest/CustomMessageSerializerTests.cs @@ -42,14 +42,54 @@ public void RoundTripMessageSerializationTest() Assert.IsType(deserializedRequest.Messages[3]); Assert.IsType(deserializedRequest.Messages[4]); Assert.IsType(deserializedRequest.Messages[5]); + } + } + + [Fact] + public void RoundTripMessageWithDataSerializationTest() + { + using (var scope = _factory.Services.CreateScope()) + { + var openAiapiClient = scope.ServiceProvider.GetService(); + CreateChatCompletionRequest request = new CreateChatCompletionRequest(); + request.AddMessage(new ChatCompletionFunctionMessage()); + request.AddMessage(new ChatCompletionSystemMessage() { Content = "system content", Name = "system"}); + request.AddMessage(new ChatCompletionAssistantMessage() { Content = "assistant content", Name = "assistant"} ); + request.AddMessage(new ChatCompletionToolMessage() { Content = "tool content", ToolCallId = Guid.NewGuid().ToString("N")}); + request.AddMessage(new ChatCompletionUserContentMessage() { Content = "user content", Name = "user"} ); + var userContents = new ChatCompletionUserContentsMessage() { Name = "user contents", }; + userContents.Contents.Add(new ContentTextItem() { Text = "what about this image?" }); + var imageitem = new ContentImageItem(); + imageitem.ImageUrl.Detail = ImageDetailLevel.High; + imageitem.ImageUrl.ImageURl = Convert.ToBase64String(Resources.Resource.pink_panther); + userContents.Contents.Add(imageitem); + //request.AddMessage(userContents); + + var json = JsonSerializer.Serialize(request); + + Assert.NotNull(json); + + var deserializedRequest = JsonSerializer.Deserialize(json); + Assert.NotNull(deserializedRequest); + Assert.IsType(deserializedRequest); + //Assert.True(deserializedRequest.Messages.Count == 6); + Assert.IsType(deserializedRequest.Messages[0]); + Assert.IsType(deserializedRequest.Messages[1]); + Assert.IsType(deserializedRequest.Messages[2]); + Assert.IsType(deserializedRequest.Messages[3]); + Assert.IsType(deserializedRequest.Messages[4]); + //Assert.IsType(deserializedRequest.Messages[5]); + + //ChatCompletionUserContentsMessage userContentsMessage = deserializedRequest.Messages[5] as ChatCompletionUserContentsMessage; + + //Assert.True(userContentsMessage.Contents.Count == 2); + //Assert.IsType(userContentsMessage.Contents[0]); + //Assert.IsType(userContentsMessage.Contents[1]); + //ContentImageItem imageItem = userContentsMessage.Contents[1] as ContentImageItem; + //Assert.True(imageItem.ImageUrl.ImageURl.Length == imageitem.ImageUrl.ImageURl.Length); - //var response = await openAiapiClient.CreateTranscriptionsAsync(request); - //Assert.NotNull(response); - //Assert.False(response.HasError,response?.ErrorResponse?.Error?.Message); - //Assert.NotNull(response?.OpenAIResponse?.Text); - //Assert.True(response.OpenAIResponse.Text.Contains("prova")); } } } From 0cba2ad196e508f7dddf9f52ca36c1a0abb5d17b Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Mon, 1 Jan 2024 12:17:20 +0100 Subject: [PATCH 07/17] wip test --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 1 + DevExtremeAI/Utils/ContentItemConverter.cs | 133 ++++++++++++++++++ DevExtremeAI/Utils/MessageJsonConverter.cs | 6 + .../CustomMessageSerializerTests.cs | 18 +-- 4 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 DevExtremeAI/Utils/ContentItemConverter.cs diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index ab0bb11..b6364b7 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -538,6 +538,7 @@ public class ToolCall /// /// Base class for content items used in Contents of ChatCompletionUserContentsMessage /// + [JsonConverter(typeof(ContentItemConverter))] public abstract class ContentItem { /// diff --git a/DevExtremeAI/Utils/ContentItemConverter.cs b/DevExtremeAI/Utils/ContentItemConverter.cs new file mode 100644 index 0000000..dab5cd6 --- /dev/null +++ b/DevExtremeAI/Utils/ContentItemConverter.cs @@ -0,0 +1,133 @@ +using DevExtremeAI.OpenAIDTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace DevExtremeAI.Utils +{ + public class ContentItemConverter : JsonConverter + { + + //TODO: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?source=recommendations&pivots=dotnet-8-0#support-polymorphic-deserialization + + public override bool CanConvert(Type typeToConvert) => + typeof(ContentItem).IsAssignableFrom(typeToConvert); + + public override ContentItem? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ContentItem? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader specializedReader = reader; + try + { + Utf8JsonReader readerClone = reader; + var itemType = FindType(ref readerClone, typeToConvert, options); + if (itemType != null) + { + switch (itemType) + { + case ContentItemTypes.Text: + { + ret = JsonSerializer.Deserialize(ref reader); + } + break; + case ContentItemTypes.ImageUrl: + { + ret = JsonSerializer.Deserialize(ref reader); + } + break; + default: + { + throw new JsonException(); + } + } + return ret; + } + throw new JsonException(); + } + finally + { + //reader = originalReader; + } + } + + private ContentItemTypes? FindType(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ContentItemTypes? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader cloneReader = reader; + + try + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + while (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.EndObject) + { + return ret; + } + + if (cloneReader.TokenType == JsonTokenType.PropertyName) + { + string? propertyName = cloneReader.GetString(); + if (propertyName == "type") + { + if (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.String) + { + var stringValue = cloneReader.GetString(); + if (!String.IsNullOrEmpty(stringValue)) + { + switch (stringValue) + { + case "text": + return ContentItemTypes.Text; + break; + case "image_url": + return ContentItemTypes.ImageUrl; + break; + } + } + } + } + + } + } + } + + } + finally + { + reader = originalReader; + } + return ret; + + } + public override void Write(Utf8JsonWriter writer, ContentItem value, JsonSerializerOptions options) + { + switch (value) + { + case ContentTextItem t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + case ContentImageItem t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; + } + } + } + +} diff --git a/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs index 6cb913f..4c21d0a 100644 --- a/DevExtremeAI/Utils/MessageJsonConverter.cs +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -91,6 +91,12 @@ public override bool CanConvert(Type typeToConvert) => return ret; } + if (cloneReader.TokenType == JsonTokenType.StartArray) + { + cloneReader.Skip(); + } + + if (cloneReader.TokenType == JsonTokenType.PropertyName) { string? propertyName = cloneReader.GetString(); diff --git a/DevExtremeAILibTest/CustomMessageSerializerTests.cs b/DevExtremeAILibTest/CustomMessageSerializerTests.cs index 79b492c..fd062a0 100644 --- a/DevExtremeAILibTest/CustomMessageSerializerTests.cs +++ b/DevExtremeAILibTest/CustomMessageSerializerTests.cs @@ -63,7 +63,7 @@ public void RoundTripMessageWithDataSerializationTest() imageitem.ImageUrl.Detail = ImageDetailLevel.High; imageitem.ImageUrl.ImageURl = Convert.ToBase64String(Resources.Resource.pink_panther); userContents.Contents.Add(imageitem); - //request.AddMessage(userContents); + request.AddMessage(userContents); var json = JsonSerializer.Serialize(request); @@ -72,23 +72,23 @@ public void RoundTripMessageWithDataSerializationTest() var deserializedRequest = JsonSerializer.Deserialize(json); Assert.NotNull(deserializedRequest); Assert.IsType(deserializedRequest); - //Assert.True(deserializedRequest.Messages.Count == 6); + Assert.True(deserializedRequest.Messages.Count == 6); Assert.IsType(deserializedRequest.Messages[0]); Assert.IsType(deserializedRequest.Messages[1]); Assert.IsType(deserializedRequest.Messages[2]); Assert.IsType(deserializedRequest.Messages[3]); Assert.IsType(deserializedRequest.Messages[4]); - //Assert.IsType(deserializedRequest.Messages[5]); + Assert.IsType(deserializedRequest.Messages[5]); - //ChatCompletionUserContentsMessage userContentsMessage = deserializedRequest.Messages[5] as ChatCompletionUserContentsMessage; + ChatCompletionUserContentsMessage userContentsMessage = deserializedRequest.Messages[5] as ChatCompletionUserContentsMessage; - //Assert.True(userContentsMessage.Contents.Count == 2); - //Assert.IsType(userContentsMessage.Contents[0]); - //Assert.IsType(userContentsMessage.Contents[1]); + Assert.True(userContentsMessage.Contents.Count == 2); + Assert.IsType(userContentsMessage.Contents[0]); + Assert.IsType(userContentsMessage.Contents[1]); - //ContentImageItem imageItem = userContentsMessage.Contents[1] as ContentImageItem; + ContentImageItem imageItem = userContentsMessage.Contents[1] as ContentImageItem; - //Assert.True(imageItem.ImageUrl.ImageURl.Length == imageitem.ImageUrl.ImageURl.Length); + Assert.True(imageItem.ImageUrl.ImageURl.Length == imageitem.ImageUrl.ImageURl.Length); } } From d9e20c47ca530d3b66d4641ad2617a5f02761bfe Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Mon, 1 Jan 2024 16:57:23 +0100 Subject: [PATCH 08/17] serialization deserialization test ok --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 7 +- ...nverter.cs => ContentItemJsonConverter.cs} | 8 +- .../Utils/ContentItemListJsonConverter.cs | 75 +++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) rename DevExtremeAI/Utils/{ContentItemConverter.cs => ContentItemJsonConverter.cs} (95%) create mode 100644 DevExtremeAI/Utils/ContentItemListJsonConverter.cs diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index b6364b7..2846d4f 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -460,7 +460,8 @@ public ChatCompletionUserContentsMessage() /// Image input is only supported when using the gpt-4-visual-preview model. /// [JsonPropertyName("content")] - public List Contents { get; private set; } = new List(); + [JsonConverter(typeof(ContentItemListJsonConverter))] + public List Contents { get; set; } = new List(); } /// @@ -538,7 +539,7 @@ public class ToolCall /// /// Base class for content items used in Contents of ChatCompletionUserContentsMessage /// - [JsonConverter(typeof(ContentItemConverter))] + [JsonConverter(typeof(ContentItemJsonConverter))] public abstract class ContentItem { /// @@ -570,7 +571,7 @@ public ContentImageItem() } [JsonPropertyName("image_url")] - public ImageUrl ImageUrl { get; private set; } = new ImageUrl(); + public ImageUrl ImageUrl { get; set; } = new ImageUrl(); } public class ImageUrl diff --git a/DevExtremeAI/Utils/ContentItemConverter.cs b/DevExtremeAI/Utils/ContentItemJsonConverter.cs similarity index 95% rename from DevExtremeAI/Utils/ContentItemConverter.cs rename to DevExtremeAI/Utils/ContentItemJsonConverter.cs index dab5cd6..9e0637e 100644 --- a/DevExtremeAI/Utils/ContentItemConverter.cs +++ b/DevExtremeAI/Utils/ContentItemJsonConverter.cs @@ -9,7 +9,7 @@ namespace DevExtremeAI.Utils { - public class ContentItemConverter : JsonConverter + public class ContentItemJsonConverter : JsonConverter { //TODO: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?source=recommendations&pivots=dotnet-8-0#support-polymorphic-deserialization @@ -68,6 +68,7 @@ public override bool CanConvert(Type typeToConvert) => throw new JsonException(); } + while (cloneReader.Read()) { if (cloneReader.TokenType == JsonTokenType.EndObject) @@ -75,6 +76,11 @@ public override bool CanConvert(Type typeToConvert) => return ret; } + if (cloneReader.TokenType == JsonTokenType.StartObject) + { + cloneReader.Skip(); + } + if (cloneReader.TokenType == JsonTokenType.PropertyName) { string? propertyName = cloneReader.GetString(); diff --git a/DevExtremeAI/Utils/ContentItemListJsonConverter.cs b/DevExtremeAI/Utils/ContentItemListJsonConverter.cs new file mode 100644 index 0000000..58a420e --- /dev/null +++ b/DevExtremeAI/Utils/ContentItemListJsonConverter.cs @@ -0,0 +1,75 @@ +using DevExtremeAI.OpenAIDTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace DevExtremeAI.Utils +{ + public class ContentItemListJsonConverter : JsonConverter> + { + + //TODO: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?source=recommendations&pivots=dotnet-8-0#support-polymorphic-deserialization + + public override bool CanConvert(Type typeToConvert) => true; + //typeof(List).IsAssignableFrom(typeToConvert); + + public override List Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? ret = null; + Utf8JsonReader originalReader = reader; + Utf8JsonReader specializedReader = reader; + try + { + Utf8JsonReader readerClone = reader; + ret = new List(); + + while (reader.Read()) + { + + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + var item = JsonSerializer.Deserialize(ref reader); + if (item != null) + { + ret.Add(item); + } + } + } + + return ret; + + } + finally + { + //reader = originalReader; + } + + } + + public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + if (value != null) + { + foreach (ContentItem item in value) + { + JsonSerializer.Serialize(writer, item, options); + } + } + + writer.WriteEndArray(); + + } + } + +} From b4ebe3fd6dabdbd4ab91eccb695b88b5d5775593 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 17:03:27 +0100 Subject: [PATCH 09/17] UML graph --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 55 +- UML/.$Chat.drawio.bkp | 418 +++++++++++ UML/.$Chat.drawio.dtmp | 1066 +++++++++++++++++++++++++++++ UML/Chat.drawio | 1066 +++++++++++++++++++++++++++++ 4 files changed, 2582 insertions(+), 23 deletions(-) create mode 100644 UML/.$Chat.drawio.bkp create mode 100644 UML/.$Chat.drawio.dtmp create mode 100644 UML/Chat.drawio diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 2846d4f..fce428f 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -1,5 +1,4 @@ -using Microsoft.VisualBasic; -using System; +using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; @@ -22,6 +21,10 @@ namespace DevExtremeAI.OpenAIDTO { + /// + /// Creates a model response for the given chat conversation. + /// + /// public class CreateChatCompletionRequest { /// @@ -38,8 +41,6 @@ public class CreateChatCompletionRequest [JsonPropertyName("messages")] [JsonConverter(typeof(MessageListJsonConverter))] public List Messages { get; set; } = new List(); - //TODO: manage hierarchy ^^^ - //TODO: add specialized AddMessage /// @@ -281,7 +282,7 @@ public void AddTool(ToolDefinition tool) /// none is the default when no functions are present.auto is the default if functions are present. /// [JsonPropertyName("tool_choice")] - public object? ToolChoice { get; private set; } + public object? ToolChoice { get; set; } /// /// auto is the default if functions are present. @@ -314,7 +315,7 @@ public abstract class ToolDefinition /// The type of the tool. Currently, only function is supported. /// [JsonPropertyName("type")] - public string Type { get; protected set; } + public string Type { get; set; } } public class FunctionTool : ToolDefinition @@ -653,6 +654,7 @@ public enum ChatCompletionMessageRoleEnum /// /// Represents a chat completion response returned by model, based on the provided input. + /// /// public class CreateChatCompletionResponse { @@ -663,10 +665,10 @@ public class CreateChatCompletionResponse public string ID { get; set; } /// - /// The object type, which is always chat.completion. + /// A list of chat completion choices. Can be more than one if n is greater than 1. /// - [JsonPropertyName("object")] - public string Object { get; set; } + [JsonPropertyName("choices")] + public List Choices { get; set; } = new List(); /// /// The Unix timestamp (in seconds) of when the chat completion was created. @@ -681,10 +683,17 @@ public class CreateChatCompletionResponse public string Model { get; set; } /// - /// A list of chat completion choices. Can be more than one if n is greater than 1. + /// This fingerprint represents the backend configuration that the model runs with. + /// Can be used in conjunction with the seed request parameter to understand when backend changes have been made that might impact determinism. /// - [JsonPropertyName("choices")] - public List Choices { get; set; } + [JsonPropertyName("system_fingerprint")] + public string SystemFingerprint { get; set; } + + /// + /// The object type, which is always chat.completion. + /// + [JsonPropertyName("object")] + public string Object { get; set; } /// /// Usage statistics for the completion request. @@ -692,16 +701,21 @@ public class CreateChatCompletionResponse [JsonPropertyName("usage")] public CreateCompletionResponseUsage? Usage { get; set; } - /// - /// This fingerprint represents the backend configuration that the model runs with. - /// Can be used in conjunction with the seed request parameter to understand when backend changes have been made that might impact determinism. - /// - [JsonPropertyName("system_fingerprint")] - public string SystemFingerprint { get; set; } } public class CreateChatCompletionResponseChoicesInner { + + /// + /// The reason the model stopped generating tokens. + /// This will be stop if the model hit a natural stop point or a provided stop sequence, + /// length if the maximum number of tokens specified in the request was reached, + /// content_filter if content was omitted due to a flag from our content filters, + /// tool_calls if the model called a tool, or function_call (deprecated) if the model called a function. + /// + [JsonPropertyName("finish_reason")] + public string? FinishReason { get; set; } + /// /// The index of the choice in the list of choices. /// @@ -717,11 +731,6 @@ public class CreateChatCompletionResponseChoicesInner [JsonPropertyName("delta")] public ChatCompletionResponseMessage? Delta { get; set; } - /// - /// The reason the model stopped generating tokens. This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified in the request was reached, content_filter if content was omitted due to a flag from our content filters, tool_calls if the model called a tool, or function_call (deprecated) if the model called a function. - /// - [JsonPropertyName("finish_reason")] - public string? FinishReason { get; set; } } public class ChatCompletionResponseMessage diff --git a/UML/.$Chat.drawio.bkp b/UML/.$Chat.drawio.bkp new file mode 100644 index 0000000..4e2f644 --- /dev/null +++ b/UML/.$Chat.drawio.bkp @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UML/.$Chat.drawio.dtmp b/UML/.$Chat.drawio.dtmp new file mode 100644 index 0000000..db9050b --- /dev/null +++ b/UML/.$Chat.drawio.dtmp @@ -0,0 +1,1066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UML/Chat.drawio b/UML/Chat.drawio new file mode 100644 index 0000000..343d321 --- /dev/null +++ b/UML/Chat.drawio @@ -0,0 +1,1066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 71d0e545d0b0401361fef933b0996b856cc0422d Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 17:05:36 +0100 Subject: [PATCH 10/17] draw --- UML/.$Chat.drawio.dtmp | 1066 ---------------------------------------- UML/Chat.drawio | 12 +- 2 files changed, 6 insertions(+), 1072 deletions(-) delete mode 100644 UML/.$Chat.drawio.dtmp diff --git a/UML/.$Chat.drawio.dtmp b/UML/.$Chat.drawio.dtmp deleted file mode 100644 index db9050b..0000000 --- a/UML/.$Chat.drawio.dtmp +++ /dev/null @@ -1,1066 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UML/Chat.drawio b/UML/Chat.drawio index 343d321..786b86a 100644 --- a/UML/Chat.drawio +++ b/UML/Chat.drawio @@ -1,4 +1,4 @@ - + @@ -340,7 +340,7 @@ - + @@ -529,19 +529,19 @@ - + - + - + @@ -551,7 +551,7 @@ - + From 66dbfd0fc9710137b77246b9a1c4150287544d2a Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 18:35:47 +0100 Subject: [PATCH 11/17] test update model wip --- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 18 ++++++- DevExtremeAI/OpenAIDTO/EditsDTO.cs | 2 +- DevExtremeAI/Utils/MessageJsonConverter.cs | 5 ++ DevExtremeAIConsoleApp/Program.cs | 2 +- DevExtremeAILibTest/AIChatCompletionTests.cs | 54 +++++++++---------- DevExtremeAILibTest/AICompletionTests.cs | 9 ++-- DevExtremeAILibTest/AIEditTests.cs | 4 +- .../Controllers/TestAIController.cs | 2 +- 8 files changed, 55 insertions(+), 41 deletions(-) diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index fce428f..e8b4a69 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -363,7 +363,7 @@ public ResponseOutputJSONFormat() } } - + [Obsolete("use descendant of ChatCompletionRoleRequestMessage or ChatCompletionRoleStringContentMessage")] public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage { /// @@ -434,6 +434,20 @@ public ChatCompletionSystemMessage() } } + /// + /// Message with text contents of the message + /// + public class ChatCompletionRoleStringContentMessage : ChatCompletionStringRequestMessage + { + public ChatCompletionRoleStringContentMessage() + { + } + + [JsonPropertyName("role")] + public ChatCompletionMessageRoleEnum Role { get { return base.Role; } set { base.Role = value; } } + } + + /// /// User Message with text contents of the message /// @@ -479,7 +493,7 @@ public ChatCompletionAssistantMessage() /// The tool calls generated by the model, such as function calls. /// [JsonPropertyName("tool_calls")] - public List ToolCalls { get; private set; } = new List(); + public List ToolCalls { get; set; } } /// diff --git a/DevExtremeAI/OpenAIDTO/EditsDTO.cs b/DevExtremeAI/OpenAIDTO/EditsDTO.cs index 9d31984..1084ad6 100644 --- a/DevExtremeAI/OpenAIDTO/EditsDTO.cs +++ b/DevExtremeAI/OpenAIDTO/EditsDTO.cs @@ -11,7 +11,7 @@ public class CreateEditRequest { /// /// ID of the model to use. - /// You can use the `text-davinci-edit-001` or `code-davinci-edit-001` model with this endpoint. + /// You can use the `gpt-4` or `code-davinci-edit-001` model with this endpoint. /// [JsonPropertyName("model")] public string Model { get; set; } diff --git a/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs index 4c21d0a..29bf181 100644 --- a/DevExtremeAI/Utils/MessageJsonConverter.cs +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -217,6 +217,11 @@ public override void Write(Utf8JsonWriter writer, ChatCompletionRoleRequestMessa JsonSerializer.Serialize(writer, t, options); } break; + case ChatCompletionRoleStringContentMessage t: + { + JsonSerializer.Serialize(writer, t, options); + } + break; } } } diff --git a/DevExtremeAIConsoleApp/Program.cs b/DevExtremeAIConsoleApp/Program.cs index 14da703..bed7b39 100644 --- a/DevExtremeAIConsoleApp/Program.cs +++ b/DevExtremeAIConsoleApp/Program.cs @@ -34,7 +34,7 @@ static async Task Main(string[] args) var openAIClient = OpenAIClientFactory.CreateInstance(); CreateChatCompletionRequest createCompletionRequest = new CreateChatCompletionRequest(); - createCompletionRequest.Model = "gpt-3.5-turbo"; + createCompletionRequest.Model = "gpt-3.5-turbo-1106"; createCompletionRequest.Temperature = temp; while (true) diff --git a/DevExtremeAILibTest/AIChatCompletionTests.cs b/DevExtremeAILibTest/AIChatCompletionTests.cs index 7e33b05..48c0a1e 100644 --- a/DevExtremeAILibTest/AIChatCompletionTests.cs +++ b/DevExtremeAILibTest/AIChatCompletionTests.cs @@ -17,8 +17,7 @@ public AIChatCompletionTests(TestApplication factory) [Theory] - //[InlineData("text-davinci-003")] - [InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-1106")] public async Task CreateChatCompletionStreamTest(string modelID) { @@ -29,9 +28,8 @@ public async Task CreateChatCompletionStreamTest(string modelID) createCompletionRequest.Model = modelID; createCompletionRequest.Temperature = 0.9; - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() { - Role = ChatCompletionMessageRoleEnum.User, Content = "I'm getting bored, what can you do for me?" }); await Task.Delay(22000); @@ -59,8 +57,7 @@ public async Task CreateChatCompletionStreamTest(string modelID) [Theory] - //[InlineData("text-davinci-003")] - [InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-1106")] public async Task CreateChatCompletionTest(string modelID) { @@ -71,9 +68,8 @@ public async Task CreateChatCompletionTest(string modelID) createCompletionRequest.Model = modelID; createCompletionRequest.Temperature = 0.9; - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage () { - Role = ChatCompletionMessageRoleEnum.User, Content = "Hi there!" }); await Task.Delay(22000); @@ -85,14 +81,19 @@ public async Task CreateChatCompletionTest(string modelID) Assert.True(response?.OpenAIResponse.Choices.Count > 0); Assert.NotNull(response?.OpenAIResponse.Usage); + ChatCompletionAssistantMessage assistantMessage = new ChatCompletionAssistantMessage() + { + Content = response?.OpenAIResponse.Choices[0].Message.Content + }; - createCompletionRequest = new CreateChatCompletionRequest(); - createCompletionRequest.Model = modelID; - createCompletionRequest.Temperature = 0.9; + createCompletionRequest.AddMessage(assistantMessage); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + //createCompletionRequest = new CreateChatCompletionRequest(); + //createCompletionRequest.Model = modelID; + //createCompletionRequest.Temperature = 0.9; + + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() { - Role = ChatCompletionMessageRoleEnum.User, Content = "I'm getting bored, what can you do for me?" }); @@ -110,7 +111,7 @@ public async Task CreateChatCompletionTest(string modelID) [Theory] - [InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-1106")] public async Task CreateChatCompletionITATest(string modelID) { @@ -121,9 +122,8 @@ public async Task CreateChatCompletionITATest(string modelID) createCompletionRequest.Model = modelID; createCompletionRequest.Temperature = 1.4; - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() { - Role = ChatCompletionMessageRoleEnum.User, Content = "Ciao, sai parlare Italiano?" }); @@ -136,16 +136,15 @@ public async Task CreateChatCompletionITATest(string modelID) Debug.WriteLine(response.OpenAIResponse.Choices[0].Message.Content); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = response.OpenAIResponse.Choices[0].Message.Role, Content = response.OpenAIResponse.Choices[0].Message.Content }); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() { - Role = ChatCompletionMessageRoleEnum.User, Content = "Qual' la capitale d'Italia?" }); @@ -158,15 +157,14 @@ public async Task CreateChatCompletionITATest(string modelID) Debug.WriteLine(response.OpenAIResponse.Choices[0].Message.Content); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = response.OpenAIResponse.Choices[0].Message.Role, Content = response.OpenAIResponse.Choices[0].Message.Content }); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() { - Role = ChatCompletionMessageRoleEnum.User, Content = "Quali cose potrei visitare li?" }); @@ -193,7 +191,7 @@ public async Task CreateChatCompletionITATest(string modelID) /// /// [Theory] - [InlineData("gpt-3.5-turbo-0613")] + [InlineData("gpt-3.5-turbo-1106")] public async Task CreateChatCompletionOneFunctionTest(string modelID) { @@ -225,7 +223,7 @@ public async Task CreateChatCompletionOneFunctionTest(string modelID) createCompletionRequest.AddFunction(func); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.User, Content = "Come sar il tempo a Venezia, Italia oggi?" @@ -273,7 +271,7 @@ public async Task CreateChatCompletionOneFunctionTest(string modelID) /// /// [Theory] - [InlineData("gpt-3.5-turbo-0613")] + [InlineData("gpt-3.5-turbo-1106")] public async Task CreateChatCompletionMultipleFunctionTest(string modelID) { @@ -335,7 +333,7 @@ public async Task CreateChatCompletionMultipleFunctionTest(string modelID) - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.User, Content = "what is the weather going to be like in Glasgow, Scotland over the next x days" @@ -348,13 +346,13 @@ public async Task CreateChatCompletionMultipleFunctionTest(string modelID) Assert.True(response.OpenAIResponse.Choices.Count > 0); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.Assistant, Content = response.OpenAIResponse.Choices[0].Message.Content }); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.User, Content = "5 days" diff --git a/DevExtremeAILibTest/AICompletionTests.cs b/DevExtremeAILibTest/AICompletionTests.cs index cf0de65..0f0a421 100644 --- a/DevExtremeAILibTest/AICompletionTests.cs +++ b/DevExtremeAILibTest/AICompletionTests.cs @@ -17,8 +17,7 @@ public AICompletionTests(TestApplication factory) [Theory] - [InlineData("text-davinci-003")] - //[InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-instruct")] public async Task CreateCompletionTest(string modelID) { @@ -46,8 +45,7 @@ public async Task CreateCompletionTest(string modelID) } [Theory] - [InlineData("text-davinci-003")] - //[InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-instruct")] public async Task CreateCompletionArrayTest(string modelID) { @@ -113,8 +111,7 @@ public async Task CreateCompletionArrayTest(string modelID) [Theory] - [InlineData("text-davinci-003")] - //[InlineData("gpt-3.5-turbo")] + [InlineData("gpt-3.5-turbo-instruct")] public async Task CreateCompletionStreamTest(string modelID) { diff --git a/DevExtremeAILibTest/AIEditTests.cs b/DevExtremeAILibTest/AIEditTests.cs index b7a7241..a74848b 100644 --- a/DevExtremeAILibTest/AIEditTests.cs +++ b/DevExtremeAILibTest/AIEditTests.cs @@ -15,7 +15,7 @@ public AIEditTests(TestApplication factory) [Theory] - [InlineData("text-davinci-edit-001")] + [InlineData("gpt-4")] public async Task CreateEditTest(string modelID) { using (var scope = _factory.Services.CreateScope()) @@ -37,7 +37,7 @@ public async Task CreateEditTest(string modelID) } } [Theory] - [InlineData("text-davinci-edit-001")] + [InlineData("gpt-4")] public async Task CreateEditMathTest(string modelID) { using (var scope = _factory.Services.CreateScope()) diff --git a/DevExtremeAIWebApp/Controllers/TestAIController.cs b/DevExtremeAIWebApp/Controllers/TestAIController.cs index bf24f46..270e046 100644 --- a/DevExtremeAIWebApp/Controllers/TestAIController.cs +++ b/DevExtremeAIWebApp/Controllers/TestAIController.cs @@ -23,7 +23,7 @@ public TestAIController(IOpenAIAPIClient openAIApiClient) public async Task Get(int id) { var completion = new CreateCompletionRequest(); - completion.Model = "text-davinci-003"; + completion.Model = "gpt-3.5-turbo-instruct"; string prompt = $"Is the number {id} even or odd?"; completion.AddCompletionPrompt(prompt); var response = await _openAIApiClient.CreateCompletionAsync(completion); From eb88676ac89a850864794ec34b605cd5eb07c1ce Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 18:40:16 +0100 Subject: [PATCH 12/17] edit is deprecated --- DevExtremeAILibTest/AIEditTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/DevExtremeAILibTest/AIEditTests.cs b/DevExtremeAILibTest/AIEditTests.cs index a74848b..ae0f56e 100644 --- a/DevExtremeAILibTest/AIEditTests.cs +++ b/DevExtremeAILibTest/AIEditTests.cs @@ -4,7 +4,8 @@ namespace DevExtremeAILibTest { - public class AIEditTests : IClassFixture + [Obsolete] + public class AIEditTests //: IClassFixture { private readonly TestApplication _factory; @@ -15,7 +16,7 @@ public AIEditTests(TestApplication factory) [Theory] - [InlineData("gpt-4")] + [InlineData("gpt-3.5-turbo-instruct")] public async Task CreateEditTest(string modelID) { using (var scope = _factory.Services.CreateScope()) @@ -37,7 +38,7 @@ public async Task CreateEditTest(string modelID) } } [Theory] - [InlineData("gpt-4")] + [InlineData("gpt-3.5-turbo-instruct")] public async Task CreateEditMathTest(string modelID) { using (var scope = _factory.Services.CreateScope()) From 397c60ef3ea315d1a8969d8802815bc7fede3d2f Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 22:55:28 +0100 Subject: [PATCH 13/17] finetune updated --- .../OpenAIClient/OpenAIAPIClient.FineTune.cs | 41 +++++- DevExtremeAI/OpenAIDTO/FineTuneDTO.cs | 136 +++++++++++++++--- DevExtremeAILibTest/AIEditTests.cs | 8 +- DevExtremeAILibTest/AIFineTuningTests.cs | 11 +- .../Resources/Resource.Designer.cs | 29 ++++ DevExtremeAILibTest/Resources/Resource.resx | 3 + .../Resources/Trivia-Tune-Conversational.json | 53 +++++++ .../Trivia-Tune-Conversational.json.bak | 53 +++++++ DevExtremeAILibTest/TestUtility.cs | 22 ++- 9 files changed, 309 insertions(+), 47 deletions(-) create mode 100644 DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json create mode 100644 DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json.bak diff --git a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs index ec7dce7..a998314 100644 --- a/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs +++ b/DevExtremeAI/OpenAIClient/OpenAIAPIClient.FineTune.cs @@ -33,7 +33,7 @@ public async Task> CreateFineTuneJobAsync(CreateFineTu var jsonContent = CreateJsonStringContent(request); - var httpResponse = await httpClient.PostAsync($"fine-tunes", jsonContent); + var httpResponse = await httpClient.PostAsync($"fine_tuning/jobs", jsonContent); if (httpResponse.IsSuccessStatusCode) { ret.OpenAIResponse = await httpResponse.Content.ReadFromJsonAsync(); @@ -56,9 +56,19 @@ public async Task> CreateFineTuneJobAsync(CreateFineTu /// /// List your organization's fine-tuning jobs /// - /// DTO with request specs. /// OpenAIResponse property contains the AI response, if an error occurs HasError is true and the Error property contains the complete error details. public async Task> GetFineTuneJobListAsync() + { + return await GetFineTuneJobListAsync(null, null); + } + + /// + /// List your organization's fine-tuning jobs + /// + /// Identifier for the last job from the previous pagination request.(Optional) + /// Number of fine-tuning jobs to retrieve.(Optional Default 20) + /// OpenAIResponse property contains the AI response, if an error occurs HasError is true and the Error property contains the complete error details. + public async Task> GetFineTuneJobListAsync(string after, int? limit) { ResponseDTO ret = new ResponseDTO(); HttpClient httpClient = CreateHttpClient(out bool doDispose); @@ -67,7 +77,25 @@ public async Task> GetFineTuneJobListAsync( FillBaseAddress(httpClient); FillAuthRequestHeaders(httpClient.DefaultRequestHeaders); - var httpResponse = await httpClient.GetAsync($"fine-tunes"); + string request = "fine_tuning/jobs"; + if (!string.IsNullOrEmpty(after) || limit.HasValue) + { + request += "?" ; + if (!string.IsNullOrEmpty(after)) + { + request += $"after={after}" ; + } + if (!string.IsNullOrEmpty(after) && limit.HasValue) + { + request += "&"; + } + if (limit.HasValue) + { + request += $"limit={limit}"; + } + } + + var httpResponse = await httpClient.GetAsync(request); if (httpResponse.IsSuccessStatusCode) { ret.OpenAIResponse = await httpResponse.Content.ReadFromJsonAsync(); @@ -87,6 +115,7 @@ public async Task> GetFineTuneJobListAsync( } } + /// /// Gets info about the fine-tune job. /// @@ -101,7 +130,7 @@ public async Task> GetFineTuneJobDataAsync(FineTuneReq FillBaseAddress(httpClient); FillAuthRequestHeaders(httpClient.DefaultRequestHeaders); - var httpResponse = await httpClient.GetAsync($"fine-tunes/{request.FineTuneId}"); + var httpResponse = await httpClient.GetAsync($"fine_tuning/jobs/{request.FineTuneId}"); if (httpResponse.IsSuccessStatusCode) { ret.OpenAIResponse = await httpResponse.Content.ReadFromJsonAsync(); @@ -135,7 +164,7 @@ public async Task> CancelFineTuneJobAsync(FineTuneRequ FillBaseAddress(httpClient); FillAuthRequestHeaders(httpClient.DefaultRequestHeaders); - var httpResponse = await httpClient.PostAsync($"fine-tunes/{request.FineTuneId}/cancel",null); + var httpResponse = await httpClient.PostAsync($"fine_tuning/jobs/{request.FineTuneId}/cancel",null); if (httpResponse.IsSuccessStatusCode) { ret.OpenAIResponse = await httpResponse.Content.ReadFromJsonAsync(); @@ -170,7 +199,7 @@ public async Task> GetFineTuneEventLis FillBaseAddress(httpClient); FillAuthRequestHeaders(httpClient.DefaultRequestHeaders); - var httpResponse = await httpClient.GetAsync($"fine-tunes/{request.FineTuneId}/events"); + var httpResponse = await httpClient.GetAsync($"fine_tuning/jobs/{request.FineTuneId}/events"); if (httpResponse.IsSuccessStatusCode) { ret.OpenAIResponse = await httpResponse.Content.ReadFromJsonAsync(); diff --git a/DevExtremeAI/OpenAIDTO/FineTuneDTO.cs b/DevExtremeAI/OpenAIDTO/FineTuneDTO.cs index 3cf42c0..bf3f470 100644 --- a/DevExtremeAI/OpenAIDTO/FineTuneDTO.cs +++ b/DevExtremeAI/OpenAIDTO/FineTuneDTO.cs @@ -122,52 +122,149 @@ public class CreateFineTuneRequest /// [JsonPropertyName("suffix")] public string? Suffix { get; set; } + + [JsonPropertyName("hyperparameters")] + public HyperParameters HyperParameters { get; set; } + } + /// + /// The hyperparameters used for the fine-tuning job. + /// + public class HyperParameters + { + /// + /// Number of examples in each batch. + /// A larger batch size means that model parameters are updated less frequently, but with lower variance. + /// + public object? BatchSize { get; set;} + public void SetAutoBatchSize() + { + BatchSize = "auto"; + } + public void SetBatchSizeValue(int value) + { + BatchSize = value; + } + + [JsonPropertyName("learning_rate_multiplier")] + public object? LearningRateMultiplier { get; set; } + public void SetAutoLearningRateMultiplier() + { + LearningRateMultiplier = "auto"; + } + public void SetLearningRateMultiplierValue(int value) + { + LearningRateMultiplier = value; + } + + [JsonPropertyName("n_epochs")] + public object? NEpochs { get; set; } + public void SetAutoNEpochs() + { + NEpochs = "auto"; + } + public void SetNEpochsValue(int value) + { + NEpochs = value; + } + + + } + + /// + /// The fine_tuning.job object represents a fine-tuning job that has been created through the API. + /// public class FineTuneData { + /// + /// The object identifier, which can be referenced in the API endpoints. + /// [JsonPropertyName("id")] public string Id { get; set; } - [JsonPropertyName("object")] - public string Object { get; set; } - - [JsonPropertyName("model")] - public string Model { get; set; } - + /// + /// The Unix timestamp (in seconds) for when the fine-tuning job was created. + /// [JsonPropertyName("created_at")] public long CreatedAt { get; set; } - [JsonPropertyName("events")] - public List Events { get; set; } + [JsonPropertyName("error")] + public object? Error { get; set; } + /// + /// The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running. + /// [JsonPropertyName("fine_tuned_model")] - public string FineTunedModel { get; set; } + public string? FineTunedModel { get; set; } - [JsonPropertyName("hyperparams")] - public HyperParam? HyperParams { get; set; } + /// + /// The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running. + /// + [JsonPropertyName("finished_at")] + public long? FinishedAt { get; set; } + /// + /// The hyperparameters used for the fine-tuning job. See the fine-tuning guide for more details + /// + [JsonPropertyName("hyperparameters")] + public HyperParameters HyperParameters { get; set; } + + /// + /// The base model that is being fine-tuned. + /// + [JsonPropertyName("model")] + public string Model { get; set; } + + /// + /// The object type, which is always "fine_tuning.job". + /// + [JsonPropertyName("object")] + public string Object { get; set; } + + /// + /// The organization that owns the fine-tuning job. + /// [JsonPropertyName("organization_id")] public string OrganizationId { get; set; } + /// + /// The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the Files API. + /// [JsonPropertyName("result_files")] - public List? ResultFiles { get; set; } + public List? ResultFiles { get; set; } + /// + /// The current status of the fine-tuning job, which can be either validating_files, queued, running, succeeded, failed, or cancelled. + /// [JsonPropertyName("status")] public string Status { get; set; } - [JsonPropertyName("validation_files")] - public List? ValidationFiles { get; set; } + /// + /// The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running. + /// + [JsonPropertyName("trained_tokens")] + public long? TrainedTokens { get; set; } + + /// + /// The file ID used for training. You can retrieve the training data with the Files API. + /// + [JsonPropertyName("training_file")] + public string TrainingFile { get; set; } - [JsonPropertyName("training_files")] - public List? TrainingFiles { get; set; } + [JsonPropertyName("validation_file")] + public string? ValidationFile { get; set; } - [JsonPropertyName("updated_at")] - public long UpdatedAt { get; set; } } + /// + /// Fine-tuning job event object + /// public class Event { + [JsonPropertyName("id")] + public string Id { get; set; } + [JsonPropertyName("object")] public string Object { get; set; } @@ -205,6 +302,9 @@ public class GetFineTuneListResponse [JsonPropertyName("data")] public List Data { get; set; } + + [JsonPropertyName("has_more")] + public bool HasMore { get; set; } } public class FineTuneRequest diff --git a/DevExtremeAILibTest/AIEditTests.cs b/DevExtremeAILibTest/AIEditTests.cs index ae0f56e..da80e9b 100644 --- a/DevExtremeAILibTest/AIEditTests.cs +++ b/DevExtremeAILibTest/AIEditTests.cs @@ -15,8 +15,8 @@ public AIEditTests(TestApplication factory) } - [Theory] - [InlineData("gpt-3.5-turbo-instruct")] + //[Theory] + //[InlineData("gpt-3.5-turbo-instruct")] public async Task CreateEditTest(string modelID) { using (var scope = _factory.Services.CreateScope()) @@ -37,8 +37,8 @@ public async Task CreateEditTest(string modelID) Assert.NotNull(completionResponse.OpenAIResponse.Usage); } } - [Theory] - [InlineData("gpt-3.5-turbo-instruct")] + //[Theory] + //[InlineData("gpt-3.5-turbo-instruct")] public async Task CreateEditMathTest(string modelID) { using (var scope = _factory.Services.CreateScope()) diff --git a/DevExtremeAILibTest/AIFineTuningTests.cs b/DevExtremeAILibTest/AIFineTuningTests.cs index 61399ec..46c37d9 100644 --- a/DevExtremeAILibTest/AIFineTuningTests.cs +++ b/DevExtremeAILibTest/AIFineTuningTests.cs @@ -37,7 +37,7 @@ public async Task FileTuningWithCancelTest() Assert.False(deleteResponse.HasError); } - var fileContent = Resources.Resource.Trivia_Tune; + var fileContent = Resources.Resource.Trivia_Tune_Conversational; var uploadResponse = await openAiapiClient.UploadFineTuningFileAsync(new UploadFineTuningFileRequest() @@ -51,8 +51,8 @@ public async Task FileTuningWithCancelTest() var createFineTuneJobResponse = await openAiapiClient.CreateFineTuneJobAsync(new CreateFineTuneRequest() { TrainingFile = uploadResponse.OpenAIResponse.FileId, - Suffix = "andrea-dev-italy-trivia-tune", - Model = "davinci" + Suffix = "Test-trivia-tune", + Model = "gpt-3.5-turbo-1106" }); Assert.False(createFineTuneJobResponse.HasError, createFineTuneJobResponse?.ErrorResponse?.Error?.Message); @@ -101,7 +101,7 @@ public async Task FileTuningWithDeleteTest() await Task.Delay(22000); } - var fileContent = Resources.Resource.Trivia_Tune; + var fileContent = Resources.Resource.Trivia_Tune_Conversational; var uploadResponse = await openAiapiClient.UploadFineTuningFileAsync(new UploadFineTuningFileRequest() @@ -116,7 +116,8 @@ public async Task FileTuningWithDeleteTest() var createFineTuneJobResponse = await openAiapiClient.CreateFineTuneJobAsync(new CreateFineTuneRequest() { TrainingFile = uploadResponse.OpenAIResponse.FileId, - Suffix = "andrea-dev-italy-trivia-tune", + Suffix = "Test-trivia-tune", + Model = "gpt-3.5-turbo-1106" }); Assert.False(createFineTuneJobResponse.HasError,createFineTuneJobResponse?.ErrorResponse?.Error?.Message); diff --git a/DevExtremeAILibTest/Resources/Resource.Designer.cs b/DevExtremeAILibTest/Resources/Resource.Designer.cs index ddf9c6c..1610187 100644 --- a/DevExtremeAILibTest/Resources/Resource.Designer.cs +++ b/DevExtremeAILibTest/Resources/Resource.Designer.cs @@ -146,5 +146,34 @@ internal static string Trivia_Tune { return ResourceManager.GetString("Trivia_Tune", resourceCulture); } } + + /// + /// Looks up a localized string similar to { + /// "messages": [ + /// { + /// "role": "user", + /// "content": " What does \"www\" stand for in a website browser?\n\n###\n\n" + /// }, + /// { + /// "role": "assistant", + /// "content": " World Wide Web###" + /// }, + /// { + /// "role": "user", + /// "content": " How long is an Olympic swimming pool (in meters)?\n\n###\n\n" + /// }, + /// { + /// "role": "assistant", + /// "content": " 50 meters###" + /// }, + /// { + /// "role": "user", + /// "content": "What countries made up the original Axis powe [rest of string was truncated]";. + /// + internal static string Trivia_Tune_Conversational { + get { + return ResourceManager.GetString("Trivia_Tune_Conversational", resourceCulture); + } + } } } diff --git a/DevExtremeAILibTest/Resources/Resource.resx b/DevExtremeAILibTest/Resources/Resource.resx index f98d504..d791088 100644 --- a/DevExtremeAILibTest/Resources/Resource.resx +++ b/DevExtremeAILibTest/Resources/Resource.resx @@ -139,4 +139,7 @@ Trivia-Tune.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + Trivia-Tune-Conversational.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json b/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json new file mode 100644 index 0000000..d27a987 --- /dev/null +++ b/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json @@ -0,0 +1,53 @@ +{"messages": [{"role": "user","content": " What does \"www\" stand for in a website browser?\n\n###\n\n"},{"role": "assistant","content": " World Wide Web###"}]} +{"messages": [{"role": "user","content": " How long is an Olympic swimming pool (in meters)?\n\n###\n\n"},{"role": "assistant","content": " 50 meters###"}]} +{"messages": [{"role": "user","content": " What countries made up the original Axis powers in World War II?\n\n###\n\n"},{"role": "assistant","content": " Germany, Italy, and Japan###"}]} +{"messages": [{"role": "user","content": " Which country do cities of Perth, Adelade & Brisbane belong to?\n\n###\n\n"},{"role": "assistant","content": " Australia###"}]} +{"messages": [{"role": "user","content": " What geometric shape is generally used for stop signs?\n\n###\n\n"},{"role": "assistant","content": " Octagon###"}]} +{"messages": [{"role": "user","content": " What is \"cynophobia\"?\n\n###\n\n"},{"role": "assistant","content": " Fear of dogs###"}]} +{"messages": [{"role": "user","content": " What punctuation mark ends an imperative sentence?\n\n###\n\n"},{"role": "assistant","content": " A period or exclamation point###"}]} +{"messages": [{"role": "user","content": " Who named the Pacific Ocean?\n\n###\n\n"},{"role": "assistant","content": " Ferdinand Magellan###"}]} +{"messages": [{"role": "user","content": " How many languages are written from right to left?\n\n###\n\n"},{"role": "assistant","content": " 12###"}]} +{"messages": [{"role": "user","content": " How many countries still have Shilling as currency? Bonus point: Which countries?\n\n###\n\n"},{"role": "assistant","content": " Four, Kenya, Uganda, Tanzania, and Somalia###"}]} +{"messages": [{"role": "user","content": " What is the name of the man who launched eBay back in 1995?\n\n###\n\n"},{"role": "assistant","content": " Pierre Omidyar###"}]} +{"messages": [{"role": "user","content": " What is the name of the biggest technology company in South Korea?\n\n###\n\n"},{"role": "assistant","content": " Samsung###"}]} +{"messages": [{"role": "user","content": " Which animal can be seen on the Porsche logo?\n\n###\n\n"},{"role": "assistant","content": " Horse###"}]} +{"messages": [{"role": "user","content": " Which monarch officially made Valentine's Day a holiday in 1537?\n\n###\n\n"},{"role": "assistant","content": " Henry VIII###"}]} +{"messages": [{"role": "user","content": " Who was the first woman to win a Nobel Prize (in 1903)?\n\n###\n\n"},{"role": "assistant","content": " Marie Curie###"}]} +{"messages": [{"role": "user","content": " The first dictionary was written by?\n\n###\n\n"},{"role": "assistant","content": " Robert Cawdrey###"}]} +{"messages": [{"role": "user","content": " Worship of Krishna is observed by which Religious Faith?\n\n###\n\n"},{"role": "assistant","content": " Hinduism###"}]} +{"messages": [{"role": "user","content": " What is the name of the World's largest ocean?\n\n###\n\n"},{"role": "assistant","content": " Pacific Ocean###"}]} +{"messages": [{"role": "user","content": " Demolition of the Berlin wall separating East and West Germany began in what year?\n\n###\n\n"},{"role": "assistant","content": " 1989###"}]} +{"messages": [{"role": "user","content": " What is the romanized Arabic word for \"moon\"?\n\n###\n\n"},{"role": "assistant","content": " Qamar###"}]} +{"messages": [{"role": "user","content": " Who was the first woman pilot to fly solo across the Atlantic?\n\n###\n\n"},{"role": "assistant","content": " Amelia Earhart###"}]} +{"messages": [{"role": "user","content": " What is a duel between three people called?\n\n###\n\n"},{"role": "assistant","content": " A truel###"}]} +{"messages": [{"role": "user","content": " In the state of Georgia, it’s illegal to eat what with a fork?\n\n###\n\n"},{"role": "assistant","content": " Fried chicken###"}]} +{"messages": [{"role": "user","content": " Which Tasmanian marsupial is known for its temper?\n\n###\n\n"},{"role": "assistant","content": " Tasmanian devil###"}]} +{"messages": [{"role": "user","content": " Iceland diverted roads to avoid disturbing communities of what?\n\n###\n\n"},{"role": "assistant","content": " Elves###"}]} +{"messages": [{"role": "user","content": " In public places in the state of Florida, what's illegal to do when wearing a swimsuit?\n\n###\n\n"},{"role": "assistant","content": " Sing###"}]} +{"messages": [{"role": "user","content": " What is the original Latin word for \"vomit\"?\n\n###\n\n"},{"role": "assistant","content": " Vomitare###"}]} +{"messages": [{"role": "user","content": " How long is New Zealand’s Ninety Mile Beach? How long is New Zealand’s Ninety Mile Beach?\n\n###\n\n"},{"role": "assistant","content": " 55 miles###"}]} +{"messages": [{"role": "user","content": " What can be broken but is never held?\n\n###\n\n"},{"role": "assistant","content": " A promise###"}]} +{"messages": [{"role": "user","content": " What does come down but never goes up?\n\n###\n\n"},{"role": "assistant","content": " Rain###"}]} +{"messages": [{"role": "user","content": " What is measured in \"Mickeys\"?\n\n###\n\n"},{"role": "assistant","content": " The speed of a computer mouse###"}]} +{"messages": [{"role": "user","content": " What is Earth's largest continent?\n\n###\n\n"},{"role": "assistant","content": " Asia###"}]} +{"messages": [{"role": "user","content": " What's the smallest country in the world?\n\n###\n\n"},{"role": "assistant","content": " Vatican City (Fun fact. It's less than two-tenths of a square mile!)###"}]} +{"messages": [{"role": "user","content": " Area 51 is located in which U S state?\n\n###\n\n"},{"role": "assistant","content": " Nevada###"}]} +{"messages": [{"role": "user","content": " What country touches the Indian Ocean, the Arabian Sea, and the Bay of Bengal?\n\n###\n\n"},{"role": "assistant","content": " India###"}]} +{"messages": [{"role": "user","content": " What's the city with the most diversity in terms of language?\n\n###\n\n"},{"role": "assistant","content": " New York City###"}]} +{"messages": [{"role": "user","content": " The ancient Phoenician city of Constantine is located in what modern-day Arab country?\n\n###\n\n"},{"role": "assistant","content": " Algeria###"}]} +{"messages": [{"role": "user","content": " Which country borders 14 nations and crosses 8 time zones?\n\n###\n\n"},{"role": "assistant","content": " Russia###"}]} +{"messages": [{"role": "user","content": " Havana is the capital of what country?\n\n###\n\n"},{"role": "assistant","content": " Cuba###"}]} +{"messages": [{"role": "user","content": " What country has the most natural lakes?\n\n###\n\n"},{"role": "assistant","content": " Canada###"}]} +{"messages": [{"role": "user","content": " Riyadh is the capital of this Middle-Eastern country.\n\n###\n\n"},{"role": "assistant","content": " The Kingdom of Saudi Arabia###"}]} +{"messages": [{"role": "user","content": " Which Central American country has a name which translates to English as “The Saviour”?\n\n###\n\n"},{"role": "assistant","content": " El Salvador###"}]} +{"messages": [{"role": "user","content": " In what country would you find Lake Bled?\n\n###\n\n"},{"role": "assistant","content": " Slovenia###"}]} +{"messages": [{"role": "user","content": " In what type of matter are atoms most tightly packed?\n\n###\n\n"},{"role": "assistant","content": " Solids###"}]} +{"messages": [{"role": "user","content": " What is the hottest planet in the solar system?\n\n###\n\n"},{"role": "assistant","content": " Venus, with a temperature of 460 °C###"}]} +{"messages": [{"role": "user","content": " What is the opposite of matter?\n\n###\n\n"},{"role": "assistant","content": " Antimatter###"}]} +{"messages": [{"role": "user","content": " Which of Newton’s Laws states that ‘for every action, there is an equal and opposite reaction?\n\n###\n\n"},{"role": "assistant","content": " The third law of motion###"}]} +{"messages": [{"role": "user","content": " In 2004, what was discovered on the island of Flores in Indonesia?\n\n###\n\n"},{"role": "assistant","content": " Remains of a hobbit-sized human###"}]} +{"messages": [{"role": "user","content": " What is the nearest planet to the sun?\n\n###\n\n"},{"role": "assistant","content": " Mercury###"}]} +{"messages": [{"role": "user","content": " What color is your blood when it’s inside your body?\n\n###\n\n"},{"role": "assistant","content": " Red, the blood contains hemoglobin, which is a red protein.###"}]} +{"messages": [{"role": "user","content": " What is the largest planet in the solar system?\n\n###\n\n"},{"role": "assistant","content": " Jupiter###"}]} +{"messages": [{"role": "user","content": " How many teeth does an adult human have?\n\n###\n\n"},{"role": "assistant","content": " 32###"}]} +{"messages": [{"role": "user","content": " What's the largest bone in the human body?\n\n###\n\n"},{"role": "assistant","content": " Femur###"}]} \ No newline at end of file diff --git a/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json.bak b/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json.bak new file mode 100644 index 0000000..128ac0e --- /dev/null +++ b/DevExtremeAILibTest/Resources/Trivia-Tune-Conversational.json.bak @@ -0,0 +1,53 @@ +{"messages": [{"role": "user","content": " What does \"www\" stand for in a website browser?\n\n###\n\n"},{"role": "assistant","content": " World Wide Web###"}]} +{"messages": [{"role": "user","content": " How long is an Olympic swimming pool (in meters)?\n\n###\n\n"},{"role": "assistant","content": " 50 meters###"}]} +{"messages": [["role": "user","content": "What countries made up the original Axis powers in World War II?\n\n###\n\n"},{"role": "assistant","content": " Germany, Italy, and Japan###"}]} +{"messages": [{"role": "user","content": " Which country do cities of Perth, Adelade & Brisbane belong to?\n\n###\n\n"},{"role": "assistant","content": " Australia###"}]} +{"messages": [{"role": "user","content": " What geometric shape is generally used for stop signs?\n\n###\n\n"},{"role": "assistant","content": " Octagon###"}]} +{"messages": [{"role": "user","content": " What is \"cynophobia\"?\n\n###\n\n"},{"role": "assistant","content": " Fear of dogs###"}]} +{"messages": [{"role": "user","content": " What punctuation mark ends an imperative sentence?\n\n###\n\n"},{"role": "assistant","content": " A period or exclamation point###"}]} +{"messages": [{"role": "user","content": " Who named the Pacific Ocean?\n\n###\n\n"},{"role": "assistant","content": " Ferdinand Magellan###"}]} +{"messages": [{"role": "user","content": " How many languages are written from right to left?\n\n###\n\n"},{"role": "assistant","content": " 12###"}]} +{"messages": [{"role": "user","content": " How many countries still have Shilling as currency? Bonus point: Which countries?\n\n###\n\n"},{"role": "assistant","content": " Four, Kenya, Uganda, Tanzania, and Somalia###"}]} +{"messages": [{"role": "user","content": " What is the name of the man who launched eBay back in 1995?\n\n###\n\n"},{"role": "assistant","content": " Pierre Omidyar###"}]} +{"messages": [{"role": "user","content": " What is the name of the biggest technology company in South Korea?\n\n###\n\n"},{"role": "assistant","content": " Samsung###"}]} +{"messages": [{"role": "user","content": " Which animal can be seen on the Porsche logo?\n\n###\n\n"},{"role": "assistant","content": " Horse###"}]} +{"messages": [{"role": "user","content": " Which monarch officially made Valentine's Day a holiday in 1537?\n\n###\n\n"},{"role": "assistant","content": " Henry VIII###"}]} +{"messages": [{"role": "user","content": " Who was the first woman to win a Nobel Prize (in 1903)?\n\n###\n\n"},{"role": "assistant","content": " Marie Curie###"}]} +{"messages": [{"role": "user","content": " The first dictionary was written by?\n\n###\n\n"},{"role": "assistant","content": " Robert Cawdrey###"}]} +{"messages": [{"role": "user","content": " Worship of Krishna is observed by which Religious Faith?\n\n###\n\n"},{"role": "assistant","content": " Hinduism###"}]} +{"messages": [{"role": "user","content": " What is the name of the World's largest ocean?\n\n###\n\n"},{"role": "assistant","content": " Pacific Ocean###"}]} +{"messages": [{"role": "user","content": " Demolition of the Berlin wall separating East and West Germany began in what year?\n\n###\n\n"},{"role": "assistant","content": " 1989###"}]} +{"messages": [{"role": "user","content": " What is the romanized Arabic word for \"moon\"?\n\n###\n\n"},{"role": "assistant","content": " Qamar###"}]} +{"messages": [{"role": "user","content": " Who was the first woman pilot to fly solo across the Atlantic?\n\n###\n\n"},{"role": "assistant","content": " Amelia Earhart###"}]} +{"messages": [{"role": "user","content": " What is a duel between three people called?\n\n###\n\n"},{"role": "assistant","content": " A truel###"}]} +{"messages": [{"role": "user","content": " In the state of Georgia, it’s illegal to eat what with a fork?\n\n###\n\n"},{"role": "assistant","content": " Fried chicken###"}]} +{"messages": [{"role": "user","content": " Which Tasmanian marsupial is known for its temper?\n\n###\n\n"},{"role": "assistant","content": " Tasmanian devil###"}]} +{"messages": [{"role": "user","content": " Iceland diverted roads to avoid disturbing communities of what?\n\n###\n\n"},{"role": "assistant","content": " Elves###"}]} +{"messages": [{"role": "user","content": " In public places in the state of Florida, what's illegal to do when wearing a swimsuit?\n\n###\n\n"},{"role": "assistant","content": " Sing###"}]} +{"messages": [{"role": "user","content": " What is the original Latin word for \"vomit\"?\n\n###\n\n"},{"role": "assistant","content": " Vomitare###"}]} +{"messages": [{"role": "user","content": " How long is New Zealand’s Ninety Mile Beach? How long is New Zealand’s Ninety Mile Beach?\n\n###\n\n"},{"role": "assistant","content": " 55 miles###"}]} +{"messages": [{"role": "user","content": " What can be broken but is never held?\n\n###\n\n"},{"role": "assistant","content": " A promise###"}]} +{"messages": [{"role": "user","content": " What does come down but never goes up?\n\n###\n\n"},{"role": "assistant","content": " Rain###"}]} +{"messages": [{"role": "user","content": " What is measured in \"Mickeys\"?\n\n###\n\n"},{"role": "assistant","content": " The speed of a computer mouse###"}]} +{"messages": [{"role": "user","content": " What is Earth's largest continent?\n\n###\n\n"},{"role": "assistant","content": " Asia###"}]} +{"messages": [{"role": "user","content": " What's the smallest country in the world?\n\n###\n\n"},{"role": "assistant","content": " Vatican City (Fun fact. It's less than two-tenths of a square mile!)###"}]} +{"messages": [{"role": "user","content": " Area 51 is located in which U S state?\n\n###\n\n"},{"role": "assistant","content": " Nevada###"}]} +{"messages": [{"role": "user","content": " What country touches the Indian Ocean, the Arabian Sea, and the Bay of Bengal?\n\n###\n\n"},{"role": "assistant","content": " India###"}]} +{"messages": [{"role": "user","content": " What's the city with the most diversity in terms of language?\n\n###\n\n"},{"role": "assistant","content": " New York City###"}]} +{"messages": [{"role": "user","content": " The ancient Phoenician city of Constantine is located in what modern-day Arab country?\n\n###\n\n"},{"role": "assistant","content": " Algeria###"}]} +{"messages": [{"role": "user","content": " Which country borders 14 nations and crosses 8 time zones?\n\n###\n\n"},{"role": "assistant","content": " Russia###"}]} +{"messages": [{"role": "user","content": " Havana is the capital of what country?\n\n###\n\n"},{"role": "assistant","content": " Cuba###"}]} +{"messages": [{"role": "user","content": " What country has the most natural lakes?\n\n###\n\n"},{"role": "assistant","content": " Canada###"}]} +{"messages": [{"role": "user","content": " Riyadh is the capital of this Middle-Eastern country.\n\n###\n\n"},{"role": "assistant","content": " The Kingdom of Saudi Arabia###"}]} +{"messages": [{"role": "user","content": " Which Central American country has a name which translates to English as “The Saviour”?\n\n###\n\n"},{"role": "assistant","content": " El Salvador###"}]} +{"messages": [{"role": "user","content": " In what country would you find Lake Bled?\n\n###\n\n"},{"role": "assistant","content": " Slovenia###"}]} +{"messages": [{"role": "user","content": " In what type of matter are atoms most tightly packed?\n\n###\n\n"},{"role": "assistant","content": " Solids###"}]} +{"messages": [{"role": "user","content": " What is the hottest planet in the solar system?\n\n###\n\n"},{"role": "assistant","content": " Venus, with a temperature of 460 °C###"}]} +{"messages": [{"role": "user","content": " What is the opposite of matter?\n\n###\n\n"},{"role": "assistant","content": " Antimatter###"}]} +{"messages": [{"role": "user","content": " Which of Newton’s Laws states that ‘for every action, there is an equal and opposite reaction?\n\n###\n\n"},{"role": "assistant","content": " The third law of motion###"}]} +{"messages": [{"role": "user","content": " In 2004, what was discovered on the island of Flores in Indonesia?\n\n###\n\n"},{"role": "assistant","content": " Remains of a hobbit-sized human###"}]} +{"messages": [{"role": "user","content": " What is the nearest planet to the sun?\n\n###\n\n"},{"role": "assistant","content": " Mercury###"}]} +{"messages": [{"role": "user","content": " What color is your blood when it’s inside your body?\n\n###\n\n"},{"role": "assistant","content": " Red, the blood contains hemoglobin, which is a red protein.###"}]} +{"messages": [{"role": "user","content": " What is the largest planet in the solar system?\n\n###\n\n"},{"role": "assistant","content": " Jupiter###"}]} +{"messages": [{"role": "user","content": " How many teeth does an adult human have?\n\n###\n\n"},{"role": "assistant","content": " 32###"}]} +{"messages": [{"role": "user","content": " What's the largest bone in the human body?\n\n###\n\n"},{"role": "assistant","content": " Femur###"}]} \ No newline at end of file diff --git a/DevExtremeAILibTest/TestUtility.cs b/DevExtremeAILibTest/TestUtility.cs index 3e7718f..c041a08 100644 --- a/DevExtremeAILibTest/TestUtility.cs +++ b/DevExtremeAILibTest/TestUtility.cs @@ -35,7 +35,7 @@ internal async Task ClearAllTestJobsAndModels() var fineTuneList = await openAiapiClient.GetFineTuneJobListAsync(); Assert.False(fineTuneList.HasError); - foreach (var fineTuneData in fineTuneList.OpenAIResponse.Data.Where(ft => ft.TrainingFiles.Any(f => f.FileName.StartsWith("Test-")))) + foreach (var fineTuneData in fineTuneList.OpenAIResponse.Data.Where(ft => !string.IsNullOrEmpty(ft?.FineTunedModel) && (ft.FineTunedModel.StartsWith("Test") || ft.FineTunedModel.Contains("trivia")) )) { if (fineTuneData.ResultFiles != null) @@ -43,26 +43,20 @@ internal async Task ClearAllTestJobsAndModels() foreach (var resultFile in fineTuneData.ResultFiles) { var deletedFile = await openAiapiClient.DeleteFileAsync(new DeleteFileRequest() - { FileId = resultFile.FileId }); + { FileId = resultFile }); } } - if (fineTuneData.ValidationFiles != null) + if (fineTuneData.ValidationFile != null) { - foreach (var validationFile in fineTuneData.ValidationFiles) - { - var deletedFile = await openAiapiClient.DeleteFileAsync(new DeleteFileRequest() - { FileId = validationFile.FileId }); - } + var deletedFile = await openAiapiClient.DeleteFileAsync(new DeleteFileRequest() + { FileId = fineTuneData.ValidationFile}); } - if (fineTuneData.TrainingFiles != null) + if (fineTuneData.TrainingFile != null) { - foreach (var trainingFile in fineTuneData.TrainingFiles) - { - var deletedFile = await openAiapiClient.DeleteFileAsync(new DeleteFileRequest() - { FileId = trainingFile.FileId }); - } + var deletedFile = await openAiapiClient.DeleteFileAsync(new DeleteFileRequest() + { FileId = fineTuneData.TrainingFile }); } if (!string.IsNullOrEmpty(fineTuneData.FineTunedModel)) From 1d0e93acbf6ce7dd09914ad23feab302756b4f7d Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 23:12:50 +0100 Subject: [PATCH 14/17] test program updated --- DevExtremeAIConsoleApp/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DevExtremeAIConsoleApp/Program.cs b/DevExtremeAIConsoleApp/Program.cs index bed7b39..4cae011 100644 --- a/DevExtremeAIConsoleApp/Program.cs +++ b/DevExtremeAIConsoleApp/Program.cs @@ -48,7 +48,7 @@ static async Task Main(string[] args) chat = chat.Substring("YOU:".Length); } - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.User, Content = chat, @@ -60,7 +60,7 @@ static async Task Main(string[] args) Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(response.OpenAIResponse.Choices[0].Message.Content); - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = response.OpenAIResponse.Choices[0].Message.Role, Content = response.OpenAIResponse.Choices[0].Message.Content From 1cccb22bb3232a38c0fedfae01c158e75c792d2d Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Tue, 2 Jan 2024 23:25:49 +0100 Subject: [PATCH 15/17] updated test webapp (it works!) --- DevExtremeAIWebApp/Controllers/TestAIController.cs | 10 +++++----- DevExtremeAIWebApp/Dockerfile | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DevExtremeAIWebApp/Controllers/TestAIController.cs b/DevExtremeAIWebApp/Controllers/TestAIController.cs index 270e046..9f035b8 100644 --- a/DevExtremeAIWebApp/Controllers/TestAIController.cs +++ b/DevExtremeAIWebApp/Controllers/TestAIController.cs @@ -22,12 +22,12 @@ public TestAIController(IOpenAIAPIClient openAIApiClient) [HttpGet("{id}")] public async Task Get(int id) { - var completion = new CreateCompletionRequest(); - completion.Model = "gpt-3.5-turbo-instruct"; + var chat = new CreateChatCompletionRequest(); + chat.Model = "gpt-3.5-turbo-1106"; string prompt = $"Is the number {id} even or odd?"; - completion.AddCompletionPrompt(prompt); - var response = await _openAIApiClient.CreateCompletionAsync(completion); - return $"{prompt} -> {response?.OpenAIResponse?.Choices[0]?.Text}"; + chat.AddMessage(new ChatCompletionUserContentMessage() { Content = prompt }); + var response = await _openAIApiClient.CreateChatCompletionAsync(chat); + return $"{prompt} -> {response?.OpenAIResponse?.Choices[0]?.Message?.Content}"; } } diff --git a/DevExtremeAIWebApp/Dockerfile b/DevExtremeAIWebApp/Dockerfile index 403ae19..e88eeea 100644 --- a/DevExtremeAIWebApp/Dockerfile +++ b/DevExtremeAIWebApp/Dockerfile @@ -1,11 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["DevExtremeAIWebApp/DevExtremeAIWebApp.csproj", "DevExtremeAIWebApp/"] RUN dotnet restore "DevExtremeAIWebApp/DevExtremeAIWebApp.csproj" From cf3bc703ea5c943d989ca38d3737fba307517fb1 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Wed, 3 Jan 2024 17:06:13 +0100 Subject: [PATCH 16/17] documentation --- .gitignore | 3 + DevExtremeAI/DevExtremeAI.csproj | 9 +- DevExtremeAI/OpenAIDTO/ChatDTO.cs | 7 +- NUGET.md | 13 +- README.md | 101 +++++++- UML/.$Chat.drawio.bkp | 418 ------------------------------ UML/Chat-ChatCompletion.svg | 3 + UML/Chat-ChatCompletionChunk.svg | 3 + UML/Chat-CreateChatCompletion.svg | 3 + 9 files changed, 111 insertions(+), 449 deletions(-) delete mode 100644 UML/.$Chat.drawio.bkp create mode 100644 UML/Chat-ChatCompletion.svg create mode 100644 UML/Chat-ChatCompletionChunk.svg create mode 100644 UML/Chat-CreateChatCompletion.svg diff --git a/.gitignore b/.gitignore index d8abf94..bd8aaa0 100644 --- a/.gitignore +++ b/.gitignore @@ -363,3 +363,6 @@ MigrationBackup/ FodyWeavers.xsd *.Development.json **/launchSettings.json + +#Draw.io backup +*.bkp \ No newline at end of file diff --git a/DevExtremeAI/DevExtremeAI.csproj b/DevExtremeAI/DevExtremeAI.csproj index fa26eea..9f2ab06 100644 --- a/DevExtremeAI/DevExtremeAI.csproj +++ b/DevExtremeAI/DevExtremeAI.csproj @@ -12,18 +12,19 @@ https://github.com/AndreaPic/DevExtremeAI True $(VersionPrefix)$(VersionSuffix) - 1.8.0 + 8.0.0 openai;dotnet;aspnet;csharp;gpt-4;gpt-3.5-turbo;davinci;DALL·E;Whisper;fine-tunes - 1.8.0.0 - 1.8.0.0 + 8.0.0.0 + 8.0.0.0 git This library is full compliant to openAI specs and also implement openAI error response. It's very easy to use with asp.net core, has full support to dependency injection and it's very easy to use in libraries without dependency injection. MIT - - (1.8.0 new) *** .net 8 *** + - (8.0.0 new) *** .net 8 *** + - (8.0.0 new) updated all new openai api, models and object models, deprecated old unsupported api - (1.1.0) *** Full functions implementation *** - (1.0.4) Added support for multiple stop sequences in completions diff --git a/DevExtremeAI/OpenAIDTO/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index e8b4a69..619b6ac 100644 --- a/DevExtremeAI/OpenAIDTO/ChatDTO.cs +++ b/DevExtremeAI/OpenAIDTO/ChatDTO.cs @@ -391,11 +391,8 @@ public class ChatCompletionRequestMessage : ChatCompletionStringRequestMessage } - //TODO: review as https://platform.openai.com/docs/api-reference/chat/create - - //TODO: stack overflow access violation https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-6-0#required-properties [System.Text.Json.Serialization.JsonConverterAttribute(typeof(MessageJsonConverter))] - public class ChatCompletionRoleRequestMessage + public abstract class ChatCompletionRoleRequestMessage { /// /// The role of the author of this message. One of system, user, or assistant. @@ -404,7 +401,7 @@ public class ChatCompletionRoleRequestMessage public ChatCompletionMessageRoleEnum Role { get; internal protected set; } } - public class ChatCompletionNameRequestMessage : ChatCompletionRoleRequestMessage + public abstract class ChatCompletionNameRequestMessage : ChatCompletionRoleRequestMessage { /// /// The name of the user in a multi-user chat diff --git a/NUGET.md b/NUGET.md index 3e4ae33..33c9643 100644 --- a/NUGET.md +++ b/NUGET.md @@ -5,7 +5,7 @@ DevextremeAI is a library with full and complete implementation of all OpenAI's APIs. This library is fully compliant to openAI specs and also implement openAI error response. It's very easy to use with asp.net core and has full support to dependency injection (with a single line of code as asp.net standard pattern). -It's easy to use also in libraries without dependency injection (see samples below). +It's easy to use also in libraries without dependency injection (see samples on github). *Please note that this is **unofficial** OpenAPI library* (It's not mantained by OpenAI Company). @@ -22,14 +22,11 @@ You can find the documentation in the [github repository](https://github.com/And Are covered all OpenAI's API types: -- Functions -- Models -- Completions +- Audio - Chat -- Edits -- Imnages - Embeddings -- Audio +- Fine-tuning - Files -- Fine-tunes +- Images +- Models - Moderations diff --git a/README.md b/README.md index 93b0626..4479ee6 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,12 @@ an example of use of IOpenAIAPIClient in controller or apicontroller could be: [HttpGet("{id}")] public async Task Get(int id) { - var completion = new CreateCompletionRequest(); - completion.Model = "text-davinci-003"; - string prompt = $"Is the number {id} even or odd?"; - completion.AddCompletionPrompt(prompt); - var response = await _openAIApiClient.CreateCompletionAsync(completion); - return $"{prompt} -> {response?.OpenAIResponse?.Choices[0]?.Text}"; + var chat = new CreateChatCompletionRequest(); + chat.Model = "gpt-3.5-turbo-1106"; + string prompt = $"Is the number {id} even or odd?"; + chat.AddMessage(new ChatCompletionUserContentMessage() { Content = prompt }); + var response = await _openAIApiClient.CreateChatCompletionAsync(chat); + return $"{prompt} -> {response?.OpenAIResponse?.Choices[0]?.Message?.Content}"; } ``` @@ -143,8 +143,8 @@ Inside your Library or Main method of the console application you can create an var openAIClient = OpenAIClientFactory.CreateInstance(); //create an instance o IOpenAIAPIClient CreateChatCompletionRequest createCompletionRequest = new CreateChatCompletionRequest(); - createCompletionRequest.Model = "gpt-3.5-turbo"; - createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() + createCompletionRequest.Model = "gpt-3.5-turbo-1106"; + createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() { Role = ChatCompletionMessageRoleEnum.User, Content = "Hello!", @@ -205,21 +205,94 @@ This type has three properties: - `OpenAIResponse` that is the same of the OpenAI response. - Every DTO has the standard .net documentation so you can find documentation in intellisense and because are the same of OpenAI you can find documentation in the [official OpenAI API Reference](https://platform.openai.com/docs/api-reference) also you can look at the integration tests in DevExtremeAILibTest directory. +## OpenAI API object models + +### Create Chat Completion + +![Create Chat Completion UML](./UML/Chat-CreateChatCompletion.svg) + +### Chat Completion Response + +![Chat Completion Response UML](./UML/Chat-ChatCompletion.svg) + +### Chat Completion Chunk Response + +![Chat Completion Chunk Response UML](./UML/Chat-ChatCompletionChunk.svg) + +## How to upgrade to release 8.0.0 + +This release support openai api and object models complaint to gpt-4 and gpt-3.5-turbo. +In this openai api version some api has been deprecated and some object model has changed. +You can see the updated object model in the UML schema. +For chat api now you can use objects that inherits from the base class 'ChatCompletionRoleRequestMessage'. + +Object ChatCompletionRoleStringContentMessage is for backward compatibility only. With this object you can set the Role property like old object model version. + +for example old code like this: + +```csharp +CreateChatCompletionRequest createCompletionRequest = new CreateChatCompletionRequest(); +createCompletionRequest.Model = "gpt-3.5-turbo"; +createCompletionRequest.Messages.Add(new ChatCompletionRequestMessage() +{ + Role = ChatCompletionMessageRoleEnum.User, + Content = "Who are you?" +}); +``` + +Can be update in this way: + +```csharp +CreateChatCompletionRequest createCompletionRequest = new CreateChatCompletionRequest(); +createCompletionRequest.Model = "gpt-3.5-turbo-1106"; +createCompletionRequest.Messages.Add(new ChatCompletionRoleStringContentMessage() +{ + Role = ChatCompletionMessageRoleEnum.User, + Content = "Who are you?" +}); +``` + +Changes are: + +- Model updated from 'gpt-3.5-turbo' to 'gpt-3.5-turbo-1106' +- 'new ChatCompletionRequestMessage()' updated with 'new ChatCompletionRoleStringContentMessage()' + +To fully update to latest version update must be done in this way: + +```csharp +CreateChatCompletionRequest createCompletionRequest = new CreateChatCompletionRequest(); +createCompletionRequest.Model = "gpt-3.5-turbo-1106"; +createCompletionRequest.Messages.Add(new ChatCompletionUserContentMessage() +{ + Content = "Who are you?" +}); +``` + +Changes are: + +- Model updated from 'gpt-3.5-turbo' to 'gpt-3.5-turbo-1106' +- 'new ChatCompletionRequestMessage()' updated with 'new ChatCompletionUserContentMessage()' +- Role property is not used (is intrinsic in its type) + ## API Types Are covered all OpenAI API types: -- Models -- Completions +- Audio - Chat -- Edits -- Images - Embeddings -- Audio +- Fine-tuning - Files -- Fine-tunes +- Images +- Models - Moderations +## Legacy API Deprecated Types + +- Completions +- Edits +- Fine-tunes + ## Examples You can find the examples of every api in test unit project. diff --git a/UML/.$Chat.drawio.bkp b/UML/.$Chat.drawio.bkp deleted file mode 100644 index 4e2f644..0000000 --- a/UML/.$Chat.drawio.bkp +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UML/Chat-ChatCompletion.svg b/UML/Chat-ChatCompletion.svg new file mode 100644 index 0000000..d1c06ad --- /dev/null +++ b/UML/Chat-ChatCompletion.svg @@ -0,0 +1,3 @@ + + +
CompletionChoice
CompletionChoice
+ finish_reason: String (FinishReasonConsts)
+ finish_reason: String (FinishReasonConst...
+ index: integer
+ index: integer
+ message: ChatCompletionMessage
+ message: ChatCompletionMessage
+ logprobs: LogProb[]
+ logprobs: LogProb[]
ChatCompletionMessage
ChatCompletionMessage
+ content: String (null)
+ content: String (null)
+ tool_calls: ToolCall[]
+ tool_calls: ToolCall[]
+ role: String
+ role: String
+ function_call: FunctionCall (Deprecated)
+ function_call: FunctionCall (Deprecate...
ChatCompletion
ChatCompletion
+ id: string (required)
+ id: string (required)
+ choices: CompletionChoice[] 
+ choices: CompletionChoice[] 
+ created: integer (unix timestamp) 
+ created: integer (unix timestamp) 
+ model: String
+ model: String
+ system_fingerprint: String
+ system_fingerprint: String
+ object: String = 'chat.completion'
+ object: String = 'chat.completion'
+ usage: UsageStatistics
+ usage: UsageStatistics
1..*
1..*

FinishReasonConsts


stop
length

content_filter

tools_calls

function_call

FinishReasonConsts...
1
1
ToolCall
ToolCall
+ id: String (required)
+ id: String (required)
+ type: String (required) = 'function'
+ type: String (required) = 'function'
+ function: FunctionCall (required)
+ function: FunctionCall (required)
1..*
1..*
FunctionCall
FunctionCall
+ name: String (required)
+ name: String (required)
+ arguments: String (required)
+ arguments: String (required)
The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
The arguments to call the function with, as generated by the model...
1
1
1..*
1..*
LogProb
LogProb
+ content: MessageContentTokensProb[]
+ content: MessageContentTokensProb[]
1..*
1..*
MessageContentTokensProb
MessageContentTokensProb
+ token: String
+ token: String
+ logprob: number
+ logprob: number
+ bytes: byte[] (null)
+ bytes: byte[] (null)
+ top_logprobs: MessageContentTokenProb[]
+ top_logprobs: MessageContentTokenProb[]
1..*
1..*
MessageContentTokenProb
MessageContentTokenProb
+ token: String
+ token: String
+ logprob: number
+ logprob: number
+ bytes: byte[] (null)
+ bytes: byte[] (null)
1..*
1..*
UsageStatistics
UsageStatistics
+ completion_tokens: integer
+ completion_tokens: integer
+ propt_tokens: integer
+ propt_tokens: integer
+ total_tokens: integer
+ total_tokens: integer
1
1
Text is not SVG - cannot display
\ No newline at end of file diff --git a/UML/Chat-ChatCompletionChunk.svg b/UML/Chat-ChatCompletionChunk.svg new file mode 100644 index 0000000..2a4a0bd --- /dev/null +++ b/UML/Chat-ChatCompletionChunk.svg @@ -0,0 +1,3 @@ + + +
CompletionChoiceChunk
CompletionChoiceChunk
+ finish_reason: String (FinishReasonConsts)
+ finish_reason: String (FinishReasonConst...
+ index: integer
+ index: integer
+ delta: ChatCompletionDelta
+ delta: ChatCompletionDelta
+ logprobs: LogProb
+ logprobs: LogProb
ChatCompletionDelta
ChatCompletionDelta
+ content: String (null)
+ content: String (null)
+ tool_calls: ToolCall[]
+ tool_calls: ToolCall[]
+ role: String
+ role: String
+ function_call: FunctionCall (Deprecated)
+ function_call: FunctionCall (Deprecate...
ChatCompletionChunk
ChatCompletionChunk
+ id: string (required)
+ id: string (required)
+ choices: CompletionChoiceChunk[] 
+ choices: CompletionChoiceChunk[] 
+ created: integer (unix timestamp) 
+ created: integer (unix timestamp) 
+ model: String
+ model: String
+ system_fingerprint: String
+ system_fingerprint: String
+ object: String = 'chat.completion'
+ object: String = 'chat.completion'
+ usage: UsageStatistics
+ usage: UsageStatistics
1..*
1..*

FinishReasonConsts


stop
length

content_filter

tools_calls

function_call

FinishReasonConsts...
1
1
ToolCall
ToolCall
+ index: integer
+ index: integer
+ id: String (required)
+ id: String (required)
+ type: String (required) = 'function'
+ type: String (required) = 'function'
+ function: FunctionCall (required)
+ function: FunctionCall (required)
1..*
1..*
FunctionCall
FunctionCall
+ name: String (required)
+ name: String (required)
+ arguments: String (required)
+ arguments: String (required)
The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
The arguments to call the function with, as generated by the model...
1
1
1..*
1..*
LogProb
LogProb
+ content: MessageContentTokensProb[]
+ content: MessageContentTokensProb[]
1..*
1..*
MessageContentTokensProb
MessageContentTokensProb
+ token: String
+ token: String
+ logprob: number
+ logprob: number
+ bytes: byte[] (null)
+ bytes: byte[] (null)
+ top_logprobs: MessageContentTokenProb[]
+ top_logprobs: MessageContentTokenProb[]
1..*
1..*
MessageContentTokenProb
MessageContentTokenProb
+ token: String
+ token: String
+ logprob: number
+ logprob: number
+ bytes: byte[] (null)
+ bytes: byte[] (null)
1..*
1..*
Text is not SVG - cannot display
\ No newline at end of file diff --git a/UML/Chat-CreateChatCompletion.svg b/UML/Chat-CreateChatCompletion.svg new file mode 100644 index 0000000..df2a39c --- /dev/null +++ b/UML/Chat-CreateChatCompletion.svg @@ -0,0 +1,3 @@ + + +
SystemMessage
SystemMessage
+ content: String (required)
+ content: String (required)
+ role: String (required-RoleConsts) = 'system'
+ role: String (required-RoleConsts) = 'sy...
+ name: String (optional)
+ name: String (optional)

RoleConsts


system
user
assistant

tool

function


RoleConsts...
UserMessage
UserMessage
+ content: String (required)
+ content: String (required)
+ role: String (required-RoleConsts) = 'user'
+ role: String (required-RoleConsts) = '...
+ name: String (optional)
+ name: String (optional)
UserMessages
UserMessages
+ content: ContentItem[] (required)
+ content: ContentItem[] (required)
+ role: String (required-RoleConsts) = 'user'
+ role: String (required-RoleConsts) = 'use...
+ name: String (optional)
+ name: String (optional)

ContentTypeConsts


text
image_url


ContentTypeConsts...
ContentItem
ContentItem
+ type: String (required-ContentTypeConsts)
+ type: String (required-ContentTypeCons...
ContentTextItem
ContentTextItem
+ type: String (= 'text')
+ type: String (= 'text')
+ text: String (required)
+ text: String (required)
ContentImageItem
ContentImageItem
+ type: String (= 'image_url')
+ type: String (= 'image_url')
+ image_url: ImageUrl (required)
+ image_url: ImageUrl (required)
1..*
1..*
ImageUrl
ImageUrl
+ url: String (required)
+ url: String (required)
+ detail: ImageDetailLevelConsts
+ detail: ImageDetailLevelConsts
1
1

ImageDetailLevelConsts


auto
low

high


ImageDetailLevelConsts...
AssistantMessage
AssistantMessage
+ content: String
+ content: String
+ role: String (required-RoleConsts) = 'assistant'
+ role: String (required-RoleConsts) = 'assistant'
+ name: String (optional)
+ name: String (optional)
+ tool_calls: ToolCall[] (optional)
+ tool_calls: ToolCall[] (optional)
+ function_call: FunctionCall (Deprecated)
+ function_call: FunctionCall (Deprecated)
The contents of the assistant message. Required unless tool_calls or function_call is specified.
The contents of the assistant message...
ToolCall
ToolCall
+ id: String (required)
+ id: String (required)
+ type: String (required) = 'function'
+ type: String (required) = 'function'
+ function: FunctionCall (required)
+ function: FunctionCall (required)
1..*
1..*
FunctionCall
FunctionCall
+ name: String (required)
+ name: String (required)
+ arguments: String (required)
+ arguments: String (required)
The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
The arguments to call the function with, as generated by the model...
1
1
ToolMessage
ToolMessage
+ content: String (required)
+ content: String (required)
+ role: String (required-RoleConsts) = 'tool'
+ role: String (required-RoleConsts) = 'tool'
+ tool_call_id: String (required)
+ tool_call_id: String (required)
FunctionMessage (Deprecated)
FunctionMessage (Deprecated)
+ content: String (required)
+ content: String (required)
+ role: String (required-RoleConsts) = 'function'
+ role: String (required-RoleConsts) = 'function'
+ name: String (required)
+ name: String (required)

<<Interface>>

IRoleMessage

+ role: String (RoleConsts)



<<Interface>>...
Look at ending 's'
Look at ending 's'
CreateChatCompletion
CreateChatCompletion
+ messages: IRoleMessage[] (required)
+ messages: IRoleMessage[] (required)
+ model: string (required)
+ model: string (required)
+ frequency_penalty: number (null)
+ frequency_penalty: number (null)
+ logit_bias: map (null)
+ logit_bias: map (null)
+ logprobs: boolean (null)
+ logprobs: boolean (null)
+ top_logprobs: boolean (null)
+ top_logprobs: boolean (null)
+ max_tokens: integer (null)
+ max_tokens: integer (null)
+ n: integer (null)
+ n: integer (null)
+ presence_penalty: number (null)
+ presence_penalty: number (null)
+ response_format: ResponseFormat (null)
+ response_format: ResponseFormat (null)
+ seed: integer (null)
+ seed: integer (null)
+ stop: string/string[] (null)
+ stop: string/string[] (null)
+ stream: boolean (null)
+ stream: boolean (null)
+ temperature: number (null)
+ temperature: number (null)
+ top_p: number (null)
+ top_p: number (null)
+ tools: ToolDefinition[] (null)
+ tools: ToolDefinition[] (null)
+ tool_choice: String/Object(FunctionToolDefinition) 
(null) 
+ tool_choice: String/Object(FunctionToolDefinition)...
+ user: String (null)
+ user: String (null)
+ function_call: String/Object (null-Deprecated)
+ function_call: String/Object (null-Deprecated)
+ functions: FunctionDefinition[] (Deprecated)
+ functions: FunctionDefinition[] (Deprecated)
1..*
1..*
ResponseFormat
ResponseFormat
+ type: String (ResponseFormatConsts-null)
+ type: String (ResponseFormatConsts-nul...

ResponseFormatConsts


text
json_object


ResponseFormatConsts...
Up to 4 sequences where the API will stop generating further tokens.
Up to 4 sequences where the API will...
1
1
ToolDefinition
ToolDefinition
+ type: String = 'function' (ResponseFormatConsts)
+ type: String = 'function' (ResponseFormatCons...
+ function: FunctionDefinition (required)
+ function: FunctionDefinition (required)
*..1
*..1

ToolDefinitionConsts


function


ToolDefinitionConsts...
FunctionDefinition
FunctionDefinition
+ description: String
+ description: String
+ name: String (required)
+ name: String (required)
+ parameters: Object (Json Schema)
+ parameters: Object (Json Schema)
https://json-schema.org/understanding-json-schema
https://platform.openai.com/docs/guides/text-generation/json-mode
https://json-schema.org/understanding-j...
*..1
*..1
https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice
https://platform.openai.com/docs/api-...

ToolChoiceConsts


none
auto


ToolChoiceConsts...
FunctionToolDefinition
FunctionToolDefinition
+ type: String = 'function' (ResponseFormatConsts)
+ type: String = 'function' (ResponseFormatCons...
+ function: FunctionNameDefinition (required)
+ function: FunctionNameDefinition (required)
1
1
1
1
FunctionNameDefinition
FunctionNameDefinition
+ name: String (required)
+ name: String (required)
1
1
1
1
1
1
*..1
*..1
1
1
Text is not SVG - cannot display
\ No newline at end of file From c9e76f76d5b2dd93dbdbee051457b2030c3021a7 Mon Sep 17 00:00:00 2001 From: AndreaPic Date: Wed, 3 Jan 2024 17:08:44 +0100 Subject: [PATCH 17/17] update yml to .net8 --- .github/workflows/dotnet-ci-cd.yml | 1 + .github/workflows/dotnet-ci.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/dotnet-ci-cd.yml b/.github/workflows/dotnet-ci-cd.yml index fcc51ca..c133d84 100644 --- a/.github/workflows/dotnet-ci-cd.yml +++ b/.github/workflows/dotnet-ci-cd.yml @@ -42,6 +42,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | + 8.0.x 7.0.x 6.0.x diff --git a/.github/workflows/dotnet-ci.yml b/.github/workflows/dotnet-ci.yml index fbf9df9..ddd1a4c 100644 --- a/.github/workflows/dotnet-ci.yml +++ b/.github/workflows/dotnet-ci.yml @@ -26,6 +26,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | + 8.0.x 7.0.x 6.0.x - name: Restore dependencies