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 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 300df6e..9f2ab06 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,33 @@ https://github.com/AndreaPic/DevExtremeAI True $(VersionPrefix)$(VersionSuffix) - 1.1.0 + 8.0.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 + 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.1.0 new) *** Full functions implementation *** + - (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 + + + + + + + + + @@ -38,7 +49,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..a998314 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 { /// @@ -28,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(); @@ -51,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); @@ -62,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(); @@ -82,6 +115,7 @@ public async Task> GetFineTuneJobListAsync( } } + /// /// Gets info about the fine-tune job. /// @@ -96,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(); @@ -130,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(); @@ -165,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/ChatDTO.cs b/DevExtremeAI/OpenAIDTO/ChatDTO.cs index 0ec2b1e..619b6ac 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; @@ -10,9 +9,22 @@ using System.Threading.Tasks; using DevExtremeAI.Utils; using System.Text.Json.Nodes; +using Microsoft.Extensions.Options; +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; +using System.Text.Json; namespace DevExtremeAI.OpenAIDTO { + /// + /// Creates a model response for the given chat conversation. + /// + /// public class CreateChatCompletionRequest { /// @@ -27,7 +39,9 @@ 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(); + /// /// Add a message to the completion @@ -35,23 +49,44 @@ public class CreateChatCompletionRequest /// The message to add public void AddMessage(ChatCompletionRequestMessage message) { + if (Messages == null) + { + Messages = new List(); + } + Messages.Add(message); + } + + /// + /// Add a message to the completion + /// + /// The message to add + public void AddMessage(ChatCompletionRoleRequestMessage message) + { + if (Messages == null) + { + Messages = new List(); + } Messages.Add(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 - { + public IList? Functions + { get { - if ( (functions == null) || (functions.Count == 0)) + if ((functions == null) || (functions.Count == 0)) { return null; } @@ -67,6 +102,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 +122,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,36 +218,408 @@ 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("seed")] + 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); + } + + + /// + /// 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("tool_choice")] + public object? ToolChoice { get; 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; set; } + } + + public class FunctionTool : ToolDefinition + { + public FunctionTool() + { + Type = "function"; + } + + [JsonPropertyName("function")] + 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")] + 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 + [Obsolete("use descendant of ChatCompletionRoleRequestMessage or ChatCompletionRoleStringContentMessage")] + 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; } + + } + [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. + /// + [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 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; + } + } + + /// + /// 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 + /// + 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")] + [JsonConverter(typeof(ContentItemListJsonConverter))] + public List Contents { get; 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; set; } + } + + /// + /// 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; } + } + + + [Obsolete("use tools instead")] + public class ChatCompletionFunctionMessage : ChatCompletionStringRequestMessage + { + public ChatCompletionFunctionMessage() + { + Role = ChatCompletionMessageRoleEnum.Function; + } + } + + 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 + /// + [JsonConverter(typeof(ContentItemJsonConverter))] + 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; 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, } @@ -249,46 +658,90 @@ 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; } - [JsonPropertyName("object")] - public string Object { 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; } = new List(); + /// + /// 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; } - [JsonPropertyName("choices")] - public List Choices { 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; } + + /// + /// The object type, which is always chat.completion. + /// + [JsonPropertyName("object")] + public string Object { get; set; } + /// + /// Usage statistics for the completion request. + /// [JsonPropertyName("usage")] public CreateCompletionResponseUsage? Usage { 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. + /// [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; } - - [JsonPropertyName("finish_reason")] - public string? FinishReason { get; set; } } public class ChatCompletionResponseMessage 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/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/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(); } + + + + } diff --git a/DevExtremeAI/Utils/ContentItemJsonConverter.cs b/DevExtremeAI/Utils/ContentItemJsonConverter.cs new file mode 100644 index 0000000..9e0637e --- /dev/null +++ b/DevExtremeAI/Utils/ContentItemJsonConverter.cs @@ -0,0 +1,139 @@ +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 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 + + 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.StartObject) + { + cloneReader.Skip(); + } + + 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/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(); + + } + } + +} 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/DevExtremeAI/Utils/MessageJsonConverter.cs b/DevExtremeAI/Utils/MessageJsonConverter.cs new file mode 100644 index 0000000..29bf181 --- /dev/null +++ b/DevExtremeAI/Utils/MessageJsonConverter.cs @@ -0,0 +1,229 @@ +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 specializedReader = reader; + try + { + Utf8JsonReader readerClone = reader; + var role = FindRole(ref readerClone, typeToConvert, options); + if (role != null) + { + switch (role) + { + case ChatCompletionMessageRoleEnum.Assistant: + { + ret = JsonSerializer.Deserialize(ref reader); + } + break; + case ChatCompletionMessageRoleEnum.Function: + { + ret = JsonSerializer.Deserialize(ref reader); + + } + break; + case ChatCompletionMessageRoleEnum.System: + { + ret = JsonSerializer.Deserialize(ref reader); + } + break; + case ChatCompletionMessageRoleEnum.Tool: + { + ret = JsonSerializer.Deserialize(ref reader); + } + break; + case ChatCompletionMessageRoleEnum.User: + { + ret = DeserializeUserRequestMessage(ref reader, 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.StartArray) + { + cloneReader.Skip(); + } + + + 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; + + } + + private ChatCompletionRoleRequestMessage? DeserializeUserRequestMessage(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + Utf8JsonReader originalReader = reader; + Utf8JsonReader cloneReader = reader; + Utf8JsonReader specializedReader = reader; + ChatCompletionRoleRequestMessage? ret = null; + try + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + while (cloneReader.Read()) + { + if (cloneReader.TokenType == JsonTokenType.EndObject) + { + if (ret == null) + { + ret = JsonSerializer.Deserialize(ref reader); + } + return ret; + } + + if (cloneReader.TokenType == JsonTokenType.PropertyName) + { + string? propertyName = cloneReader.GetString(); + if (propertyName == nameof(ChatCompletionUserContentMessage.Content).ToLower()) + { + cloneReader.Read(); + if (cloneReader.TokenType == JsonTokenType.String) + { + ret = JsonSerializer.Deserialize(ref reader); + } + else if (cloneReader.TokenType == JsonTokenType.StartArray) + { + ret = JsonSerializer.Deserialize(ref reader); + } + if (ret != null) + { + break; + } + } + } + } + } + finally + { + //reader = originalReader; + } + + return ret; + + } + + public override void Write(Utf8JsonWriter writer, ChatCompletionRoleRequestMessage value, JsonSerializerOptions 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; + case ChatCompletionRoleStringContentMessage 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..11b1398 --- /dev/null +++ b/DevExtremeAI/Utils/MessageListJsonConverter.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 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 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 (ChatCompletionRoleRequestMessage item in value) + { + JsonSerializer.Serialize(writer, item, options); + } + } + + writer.WriteEndArray(); + + } + } + +} 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/DevExtremeAIConsoleApp/Program.cs b/DevExtremeAIConsoleApp/Program.cs index 14da703..4cae011 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) @@ -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 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..da80e9b 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; @@ -14,8 +15,8 @@ public AIEditTests(TestApplication factory) } - [Theory] - [InlineData("text-davinci-edit-001")] + //[Theory] + //[InlineData("gpt-3.5-turbo-instruct")] public async Task CreateEditTest(string modelID) { using (var scope = _factory.Services.CreateScope()) @@ -36,8 +37,8 @@ public async Task CreateEditTest(string modelID) Assert.NotNull(completionResponse.OpenAIResponse.Usage); } } - [Theory] - [InlineData("text-davinci-edit-001")] + //[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/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/CustomMessageSerializerTests.cs b/DevExtremeAILibTest/CustomMessageSerializerTests.cs new file mode 100644 index 0000000..fd062a0 --- /dev/null +++ b/DevExtremeAILibTest/CustomMessageSerializerTests.cs @@ -0,0 +1,96 @@ +using Microsoft.Extensions.DependencyInjection; +using DevExtremeAI.OpenAIDTO; +using DevExtremeAI.OpenAIClient; +using System.Text.Json; + +namespace DevExtremeAILibTest +{ + public class CustomMessageSerializerTests : IClassFixture + { + private readonly TestApplication _factory; + + public CustomMessageSerializerTests(TestApplication factory) + { + _factory = factory; + } + + [Fact] + 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]); + } + } + + [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); + + } + } + } +} \ No newline at end of file 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/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)) diff --git a/DevExtremeAIWebApp/Controllers/TestAIController.cs b/DevExtremeAIWebApp/Controllers/TestAIController.cs index bf24f46..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 = "text-davinci-003"; + 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/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 + + + + + + + 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" 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-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 diff --git a/UML/Chat.drawio b/UML/Chat.drawio new file mode 100644 index 0000000..786b86a --- /dev/null +++ b/UML/Chat.drawio @@ -0,0 +1,1066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +