diff --git a/OpenAI.Playground/ConsoleExtensions.cs b/OpenAI.Playground/ExtensionsAndHelpers/ConsoleExtensions.cs similarity index 61% rename from OpenAI.Playground/ConsoleExtensions.cs rename to OpenAI.Playground/ExtensionsAndHelpers/ConsoleExtensions.cs index bbf83c8b..816de5d7 100644 --- a/OpenAI.Playground/ConsoleExtensions.cs +++ b/OpenAI.Playground/ExtensionsAndHelpers/ConsoleExtensions.cs @@ -1,8 +1,8 @@ -namespace OpenAI.Playground; +namespace OpenAI.Playground.ExtensionsAndHelpers; public static class ConsoleExtensions { - public static void WriteLine(string value, ConsoleColor color) + public static void WriteLine(string? value, ConsoleColor color = ConsoleColor.Gray) { var defaultColor = Console.ForegroundColor; Console.ForegroundColor = color; diff --git a/OpenAI.Playground/ExtensionsAndHelpers/StringExtension.cs b/OpenAI.Playground/ExtensionsAndHelpers/StringExtension.cs new file mode 100644 index 00000000..fea3cf4d --- /dev/null +++ b/OpenAI.Playground/ExtensionsAndHelpers/StringExtension.cs @@ -0,0 +1,16 @@ +using System.Text.Json; + +namespace OpenAI.Playground.ExtensionsAndHelpers; + +public static class StringExtension +{ + public static string? ToJson(this object? s) + { + return s == null ? null : JsonSerializer.Serialize(s); + } + + public static T? D(this string json) where T : class + { + return string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize(json); + } +} \ No newline at end of file diff --git a/OpenAI.Playground/OpenAI.Playground.csproj b/OpenAI.Playground/OpenAI.Playground.csproj index bb245deb..05e050cc 100644 --- a/OpenAI.Playground/OpenAI.Playground.csproj +++ b/OpenAI.Playground/OpenAI.Playground.csproj @@ -59,6 +59,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/OpenAI.Playground/Program.cs b/OpenAI.Playground/Program.cs index 8af03599..6f5f26b2 100644 --- a/OpenAI.Playground/Program.cs +++ b/OpenAI.Playground/Program.cs @@ -18,8 +18,10 @@ // It is in Beta version, if you don't want to use it just comment out below line. serviceCollection.AddLaserCatEyesHttpClientListener(); +//if you want to use beta services you have to set UseBeta to true. Otherwise, it will use the stable version of OpenAI apis. +serviceCollection.AddOpenAIService(r=>r.UseBeta = true); -serviceCollection.AddOpenAIService(); +//serviceCollection.AddOpenAIService(); //// DeploymentId and ResourceName are only for Azure OpenAI. If you want to use Azure OpenAI services you have to set Provider type To Azure. //serviceCollection.AddOpenAIService(options => //{ @@ -39,9 +41,22 @@ // | / \ / \ | \ /) | ( \ /o\ / ) | (\ / | / \ / \ | // |-----------------------------------------------------------------------| -//await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk); +await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk); //await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk); + +//Assistants - BETA +//await AssistantTestHelper.RunAssistantApiTest(sdk); +//await AssistantTestHelper.RunHowAssistantsWorkTest(sdk); + +//await MessageTestHelper.RunMessageCreateTest(sdk); + +//await ThreadTestHelper.RunThreadCreateTest(sdk); +//await ThreadTestHelper.RunThreadRetrieveTest(sdk); + +//await RunTestHelper.RunRunCreateTest(sdk); +//await RunTestHelper.RunRunCancelTest(sdk); + // Vision //await VisionTestHelper.RunSimpleVisionTest(sdk); //await VisionTestHelper.RunSimpleVisionStreamTest(sdk); @@ -49,7 +64,7 @@ // Tools //await ChatCompletionTestHelper.RunChatFunctionCallTest(sdk); -await ChatCompletionTestHelper.RunChatFunctionCallTestAsStream(sdk); +//await ChatCompletionTestHelper.RunChatFunctionCallTestAsStream(sdk); // Whisper //await AudioTestHelper.RunSimpleAudioCreateTranscriptionTest(sdk); diff --git a/OpenAI.Playground/SampleData/betterway_corp.csv b/OpenAI.Playground/SampleData/betterway_corp.csv new file mode 100644 index 00000000..f10398e0 --- /dev/null +++ b/OpenAI.Playground/SampleData/betterway_corp.csv @@ -0,0 +1,2 @@ +Company Overview,Company Address +"Please refer to Betterway Supply Chain Co., Ltd. for details.","Betterway Supply Chain Co., Ltd. is located in Jiangsu Province, Nanjing City, Xianlin District, No. 615 on the Eighth Street. Established in 2011, it is a company specializing in the provision of innovative supply chain services, covering the whole country and overseas, mainly in finance, insurance, telecommunications, technology, health, education, agriculture, tourism, automotive, new materials, Australia, Brazil, etc." diff --git a/OpenAI.Playground/SampleData/betterway_corp.jsonl b/OpenAI.Playground/SampleData/betterway_corp.jsonl new file mode 100644 index 00000000..d93acadf --- /dev/null +++ b/OpenAI.Playground/SampleData/betterway_corp.jsonl @@ -0,0 +1 @@ +{"messages":[{"role":"system","content":"You are a professional assistant providing logistics supply chain services"}, {"role":"user","content":"Where exactly is Zhejiang Jiacheng Supply Chain Co., Ltd. located?"}, {"role":"assistant","content":"Zhejiang Jiacheng Supply Chain Co., Ltd. is located at No. 615, Bayi North Street, Wucheng District, Jinhua City, Zhejiang Province, Jiacheng International Business Center"}, {"role":"user","content":"Please introduce Zhejiang Jiacheng Supply Chain Company."}, {"role":"assistant","content":"Zhejiang Jiacheng Supply Chain Company was founded in 2011 and is an innovative supply chain service enterprise focusing on cross-border logistics. Its subsidiaries are located throughout the country and overseas, mainly involving Jinhua, Yiwu, Ningbo, Shenyang, Tianjin, Qingdao, Jinan, Suzhou, Chengdu, Singapore, Germany, Russia and other places."}]} \ No newline at end of file diff --git a/OpenAI.Playground/TestHelpers/AssistantTestHelper.cs b/OpenAI.Playground/TestHelpers/AssistantTestHelper.cs new file mode 100644 index 00000000..208e5156 --- /dev/null +++ b/OpenAI.Playground/TestHelpers/AssistantTestHelper.cs @@ -0,0 +1,333 @@ +using System.Text; +using OpenAI.Builders; +using OpenAI.Interfaces; +using OpenAI.ObjectModels; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; +using OpenAI.Playground.ExtensionsAndHelpers; + +namespace OpenAI.Playground.TestHelpers; + +internal static class AssistantTestHelper +{ + /// + /// Test Assistant api + /// + /// + /// + public static async Task RunAssistantApiTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Assistant APT Testing is starting:"); + + #region Create assistant + + var func = new FunctionDefinitionBuilder("get_corp_location", "get location of corp").AddParameter("name", PropertyDefinition.DefineString("company name, e.g. Betterway")) + .Validate() + .Build(); + + ConsoleExtensions.WriteLine("Assistant Create Test:", ConsoleColor.DarkCyan); + var assistantResult = await sdk.Beta.Assistants.AssistantCreate(new AssistantCreateRequest + { + Instructions = "You are a professional assistant who provides company information. Company-related data comes from uploaded questions and does not provide vague answers, only clear answers.", + Name = "Qicha", + Tools = new List() { ToolDefinition.DefineCodeInterpreter(), ToolDefinition.DefineRetrieval(), ToolDefinition.DefineFunction(func) }, + Model = Models.Gpt_3_5_Turbo_1106 + }); + if (assistantResult.Successful) + { + var assistant = assistantResult; + ConsoleExtensions.WriteLine(assistant.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{assistantResult.Error?.Code}: {assistantResult.Error?.Message}"); + return; + } + + var assistantId = assistantResult.Id; + ConsoleExtensions.WriteLine($"assistantId:{assistantId} "); + + #endregion + + + #region // Assistant List + + ConsoleExtensions.WriteLine("Assistant list:", ConsoleColor.DarkCyan); + var asstListResult = await sdk.Beta.Assistants.AssistantList(); + if (asstListResult.Successful) + { + ConsoleExtensions.WriteLine($"asst list: {asstListResult.Data?.ToJson()}"); + } + else + { + ConsoleExtensions.WriteLine($"{asstListResult.Error?.Code}: {asstListResult.Error?.Message}"); + return; + } + + #endregion + + #region // Assistant modify + + ConsoleExtensions.WriteLine("Assistant modify:", ConsoleColor.DarkCyan); + var asstResult = await sdk.Beta.Assistants.AssistantModify(assistantId, new AssistantModifyRequest() + { + Name = "Qicha rename" + }); + if (asstResult.Successful) + { + ConsoleExtensions.WriteLine(asstResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{asstResult.Error?.Code}: {asstResult.Error?.Message}"); + return; + } + + #endregion + + #region // Assistant retrieve + + ConsoleExtensions.WriteLine("Assistant retrieve:", ConsoleColor.DarkCyan); + var asstRetrieveResult = await sdk.Beta.Assistants.AssistantRetrieve(assistantId); + if (asstRetrieveResult.Successful) + { + ConsoleExtensions.WriteLine(asstRetrieveResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{asstRetrieveResult.Error?.Code}: {asstRetrieveResult.Error?.Message}"); + return; + } + + #endregion + + #region // Assistant delete + + ConsoleExtensions.WriteLine("Assistant delete:", ConsoleColor.DarkCyan); + var deleteResult = await sdk.Beta.Assistants.AssistantDelete(assistantId); + if (deleteResult.Successful) + { + ConsoleExtensions.WriteLine(deleteResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{deleteResult.Error?.Code}: {deleteResult.Error?.Message}"); + } + + #endregion + } + + /// + /// How Assistants work + /// see how-it-works + /// + /// + /// + public static async Task RunHowAssistantsWorkTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("How assistant work Testing is starting:", ConsoleColor.DarkCyan); + + #region //upload file + + const string fileName = "betterway_corp.csv"; + var sampleFile = await FileExtensions.ReadAllBytesAsync($"SampleData/{fileName}"); + var sampleFileAsString = Encoding.UTF8.GetString(sampleFile); + + ConsoleExtensions.WriteLine($"Uploading file: {fileName}", ConsoleColor.DarkCyan); + var uploadFilesResponse = await sdk.Files.FileUpload(UploadFilePurposes.UploadFilePurpose.Assistants, sampleFile, fileName); + if (uploadFilesResponse.Successful) + { + ConsoleExtensions.WriteLine($"{fileName} uploaded", ConsoleColor.DarkGreen); + } + else + { + ConsoleExtensions.WriteLine($"{fileName} failed", ConsoleColor.DarkRed); + return; + } + + var uplaodFileId = uploadFilesResponse.Id; + ConsoleExtensions.WriteLine($"uplaodFileId:{uplaodFileId}, purpose:{uploadFilesResponse.Purpose}"); + + #endregion + + #region //create assistants + + var func = new FunctionDefinitionBuilder("get_corp_location", "get location of corp").AddParameter("name", PropertyDefinition.DefineString("company name, e.g. Betterway")) + .Validate() + .Build(); + + ConsoleExtensions.WriteLine("Assistant Create Test:", ConsoleColor.DarkCyan); + var assistantResult = await sdk.Beta.Assistants.AssistantCreate(new AssistantCreateRequest + { + Instructions = "You are a professional assistant who provides company information. Company-related data comes from uploaded questions and does not provide vague answers, only clear answers.", + Name = "Qicha", + Model = Models.Gpt_3_5_Turbo_1106, + Tools = new List() { ToolDefinition.DefineCodeInterpreter(), ToolDefinition.DefineRetrieval(), ToolDefinition.DefineFunction(func) }, + FileIds = new List() { uplaodFileId } + }); + + if (assistantResult.Successful) + { + ConsoleExtensions.WriteLine(assistantResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{assistantResult.Error?.Code}: {assistantResult.Error?.Message}"); + return; + } + + var assistantId = assistantResult.Id; + ConsoleExtensions.WriteLine($"assistantId:{assistantId} "); + + #endregion + + #region //create thread + + ConsoleExtensions.WriteLine("Thread Create Test:", ConsoleColor.DarkCyan); + var threadResult = await sdk.Beta.Threads.ThreadCreate(); + if (threadResult.Successful) + { + ConsoleExtensions.WriteLine(threadResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{threadResult.Error?.Code}: {threadResult.Error?.Message}"); + return; + } + + var threadId = threadResult.Id; + ConsoleExtensions.WriteLine($"threadId: {threadId}"); + + #endregion + + #region //create thread message + + ConsoleExtensions.WriteLine("Message Create Test:", ConsoleColor.DarkCyan); + var messageResult = await sdk.Beta.Messages.MessageCreate(threadId, new MessageCreateRequest + { + Role = StaticValues.AssistantsStatics.MessageStatics.Roles.User, + Content = "Where is Zhejiang Jiacheng Supply Chain Co., LTD.", + FileIds = new List() { uplaodFileId } + }); + + if (messageResult.Successful) + { + ConsoleExtensions.WriteLine(messageResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{messageResult.Error?.Code}: {messageResult.Error?.Message}"); + return; + } + + var messageId = messageResult.Id; + + #endregion + + #region //create run + + ConsoleExtensions.WriteLine("Run Create Test:", ConsoleColor.DarkCyan); + var runResult = await sdk.Beta.Runs.RunCreate(threadId, new RunCreateRequest() + { + AssistantId = assistantId + }); + if (runResult.Successful) + { + ConsoleExtensions.WriteLine(runResult.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{runResult.Error?.Code}: {runResult.Error?.Message}"); + return; + } + + var runId = runResult.Id; + ConsoleExtensions.WriteLine($"runId: {runId}"); + + #endregion + + #region //waiting for run completed + + ConsoleExtensions.WriteLine("waiting for run completed:", ConsoleColor.DarkCyan); + var runningStatusList = new List() + { + StaticValues.AssistantsStatics.RunStatus.Queued, + StaticValues.AssistantsStatics.RunStatus.InProgress, + StaticValues.AssistantsStatics.RunStatus.RequiresAction + }; + + //Get task information + var runRetrieveResult = await sdk.Beta.Runs.RunRetrieve(threadId, runId); + while (runningStatusList.Contains(runRetrieveResult.Status)) + { + /* + * When a run has the status: "requires_action" and required_action.type is submit_tool_outputs, + * this endpoint can be used to submit the outputs from the tool calls once they're all completed. + * All outputs must be submitted in a single request. + */ + var requireAction = runRetrieveResult.RequiredAction; + if (runRetrieveResult.Status == StaticValues.AssistantsStatics.RunStatus.RequiresAction && requireAction != null && requireAction.Type == StaticValues.AssistantsStatics.RequiredActionTypes.SubmitToolOutputs) + { + var myFunc = new List() { "get_corp_location" }; + var toolOutputs = new List(); + foreach (var toolCall in requireAction.SubmitToolOutputs.ToolCalls) + { + ConsoleExtensions.WriteLine($"ToolCall:{toolCall?.ToJson()}"); + if (toolCall?.FunctionCall == null) continue; + + var funcName = toolCall.FunctionCall.Name; + if (myFunc.Contains(funcName)) + { + //do sumbit tool + var toolOutput = new ToolOutput() + { + ToolCallId = toolCall.Id, + Output = @"Zhejiang Jiacheng Supply Chain Co., Ltd. is located in Jiacheng International Business Center, +No.615 Bayi North Street, Wucheng District, Jinhua City, Zhejiang Province" + }; + toolOutputs.Add(toolOutput); + } + } + + //All outputs must be submitted in a single request. + if (toolOutputs.Any()) + { + await sdk.Beta.Runs.RunSubmitToolOutputs(threadId, runId, new SubmitToolOutputsToRunRequest() + { + ToolOutputs = toolOutputs + }); + } + + await Task.Delay(500); + } + + runRetrieveResult = await sdk.Beta.Runs.RunRetrieve(threadId, runId); + if (!runningStatusList.Contains(runRetrieveResult.Status)) + { + break; + } + } + + #endregion + + #region // message list + + //Get the final message result + ConsoleExtensions.WriteLine("Message list:", ConsoleColor.DarkCyan); + var messageListResult = await sdk.Beta.Messages.MessageList(threadId); + if (messageListResult.Successful) + { + var msgRespList = messageListResult.Data; + var ask = msgRespList?.FirstOrDefault(msg => msg.Role == StaticValues.AssistantsStatics.MessageStatics.Roles.User); + var replys = msgRespList?.Where(msg => msg.CreatedAt > ask?.CreatedAt && msg.Role == StaticValues.AssistantsStatics.MessageStatics.Roles.Assistant) + .ToList() ?? new List(); + ConsoleExtensions.WriteLine(replys.ToJson()); + } + else + { + ConsoleExtensions.WriteLine($"{messageListResult.Error?.Code}: {messageListResult.Error?.Message}"); + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenAI.Playground/TestHelpers/AudioTestHelper.cs b/OpenAI.Playground/TestHelpers/AudioTestHelper.cs index d41cb0a1..a06b77b4 100644 --- a/OpenAI.Playground/TestHelpers/AudioTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/AudioTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/ChatCompletionTestHelper.cs b/OpenAI.Playground/TestHelpers/ChatCompletionTestHelper.cs index 303b2ac1..aa509190 100644 --- a/OpenAI.Playground/TestHelpers/ChatCompletionTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/ChatCompletionTestHelper.cs @@ -3,6 +3,7 @@ using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; using OpenAI.ObjectModels.SharedModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/CompletionTestHelper.cs b/OpenAI.Playground/TestHelpers/CompletionTestHelper.cs index 0d12bb95..0837e21b 100644 --- a/OpenAI.Playground/TestHelpers/CompletionTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/CompletionTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/EditTestHelper.cs b/OpenAI.Playground/TestHelpers/EditTestHelper.cs index d057358f..7ec0fec1 100644 --- a/OpenAI.Playground/TestHelpers/EditTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/EditTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs b/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs index 0fd69129..66068d42 100644 --- a/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/FileTestHelper.cs b/OpenAI.Playground/TestHelpers/FileTestHelper.cs index 62759630..a965f5f9 100644 --- a/OpenAI.Playground/TestHelpers/FileTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/FileTestHelper.cs @@ -1,6 +1,7 @@ using System.Text; using OpenAI.Interfaces; using OpenAI.ObjectModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs b/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs index a1b119d4..93ef4dd9 100644 --- a/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs @@ -2,6 +2,7 @@ using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/FineTuningTestHelper.cs b/OpenAI.Playground/TestHelpers/FineTuningTestHelper.cs index 4e1fcd40..78e5858f 100644 --- a/OpenAI.Playground/TestHelpers/FineTuningTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/FineTuningTestHelper.cs @@ -2,6 +2,7 @@ using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; using OpenAI.ObjectModels.ResponseModels.FineTuneResponseModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/ImageTestHelper.cs b/OpenAI.Playground/TestHelpers/ImageTestHelper.cs index b90fa7ee..bc41e739 100644 --- a/OpenAI.Playground/TestHelpers/ImageTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/ImageTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/MessageTestHelper.cs b/OpenAI.Playground/TestHelpers/MessageTestHelper.cs new file mode 100644 index 00000000..af5e472b --- /dev/null +++ b/OpenAI.Playground/TestHelpers/MessageTestHelper.cs @@ -0,0 +1,62 @@ +using OpenAI.Interfaces; +using OpenAI.ObjectModels; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; + +namespace OpenAI.Playground.TestHelpers; + +internal static class MessageTestHelper +{ + public static async Task RunMessageCreateTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Message create Testing is starting:", ConsoleColor.Cyan); + + try + { + ConsoleExtensions.WriteLine("Message Create Test:", ConsoleColor.DarkCyan); + + var threadResult = await sdk.Beta.Threads.ThreadCreate(); + if (threadResult.Successful) + { + ConsoleExtensions.WriteLine(threadResult.ToJson()); + } + else + { + if (threadResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{threadResult.Error.Code}: {threadResult.Error.Message}"); + } + + var threadId = threadResult.Id; + ConsoleExtensions.WriteLine($"threadId :{threadId}"); + + var messageResult = await sdk.Beta.Messages.MessageCreate(threadId, new MessageCreateRequest + { + Role = StaticValues.AssistantsStatics.MessageStatics.Roles.User, + Content = "How does AI work? Explain it in simple terms." + }); + + if (messageResult.Successful) + { + ConsoleExtensions.WriteLine(messageResult.ToJson()); + } + else + { + if (messageResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{messageResult.Error.Code}: {messageResult.Error.Message}"); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } +} \ No newline at end of file diff --git a/OpenAI.Playground/TestHelpers/ModelTestHelper.cs b/OpenAI.Playground/TestHelpers/ModelTestHelper.cs index 80d95690..1206a2a1 100644 --- a/OpenAI.Playground/TestHelpers/ModelTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/ModelTestHelper.cs @@ -1,4 +1,5 @@ using OpenAI.Interfaces; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/ModerationTestHelper.cs b/OpenAI.Playground/TestHelpers/ModerationTestHelper.cs index 94feddf0..1c47f049 100644 --- a/OpenAI.Playground/TestHelpers/ModerationTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/ModerationTestHelper.cs @@ -1,5 +1,6 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/RunTestHelper.cs b/OpenAI.Playground/TestHelpers/RunTestHelper.cs new file mode 100644 index 00000000..7b9e810d --- /dev/null +++ b/OpenAI.Playground/TestHelpers/RunTestHelper.cs @@ -0,0 +1,137 @@ +using OpenAI.Builders; +using OpenAI.Interfaces; +using OpenAI.ObjectModels; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; +using OpenAI.Playground.ExtensionsAndHelpers; + +namespace OpenAI.Playground.TestHelpers; + +internal static class RunTestHelper +{ + public static async Task RunRunCreateTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Run create Testing is starting:", ConsoleColor.Cyan); + + + try + { + ConsoleExtensions.WriteLine("Run Create Test:", ConsoleColor.DarkCyan); + var threadResult = await sdk.Beta.Threads.ThreadCreate(); + var threadId = threadResult.Id; + var func = new FunctionDefinitionBuilder("get_corp_location", "get location of corp").AddParameter("name", PropertyDefinition.DefineString("company name, e.g. Betterway")) + .Validate() + .Build(); + var assistantResult = await sdk.Beta.Assistants.AssistantCreate(new AssistantCreateRequest + { + Instructions = "You are a professional assistant who provides company information. Company-related data comes from uploaded questions and does not provide vague answers, only clear answers.", + Name = "Qicha", + Tools = new List() { ToolDefinition.DefineCodeInterpreter(), ToolDefinition.DefineRetrieval(), ToolDefinition.DefineFunction(func) }, + Model = Models.Gpt_3_5_Turbo_1106 + }); + var runResult = await sdk.Beta.Runs.RunCreate(threadId, new RunCreateRequest() + { + AssistantId = assistantResult.Id, + }); + if (runResult.Successful) + { + ConsoleExtensions.WriteLine(runResult.ToJson()); + } + else + { + if (runResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{runResult.Error.Code}: {runResult.Error.Message}"); + } + + var runId = runResult.Id; + ConsoleExtensions.WriteLine($"runId: {runId}"); + + var doneStatusList = new List() + { StaticValues.AssistantsStatics.RunStatus.Cancelled, StaticValues.AssistantsStatics.RunStatus.Completed, StaticValues.AssistantsStatics.RunStatus.Failed, StaticValues.AssistantsStatics.RunStatus.Expired }; + var runStatus = StaticValues.AssistantsStatics.RunStatus.Queued; + var attemptCount = 0; + var maxAttempts = 10; + + do + { + var runRetrieveResult = await sdk.Beta.Runs.RunRetrieve(threadId, runId); + runStatus = runRetrieveResult.Status; + if (doneStatusList.Contains(runStatus)) + { + break; + } + + var requireAction = runRetrieveResult.RequiredAction; + if (runStatus == StaticValues.AssistantsStatics.RunStatus.RequiresAction && requireAction.Type == StaticValues.AssistantsStatics.RequiredActionTypes.SubmitToolOutputs) + { + var toolCalls = requireAction.SubmitToolOutputs.ToolCalls; + foreach (var toolCall in toolCalls) + { + ConsoleExtensions.WriteLine($"ToolCall:{toolCall?.ToJson()}"); + if (toolCall.FunctionCall == null) return; + + var funcName = toolCall.FunctionCall.Name; + if (funcName == "get_corp_location") + { + await sdk.Beta.Runs.RunCancel(threadId, runRetrieveResult.Id); + // Do submit tool + } + } + } + + await Task.Delay(1000); + attemptCount++; + if (attemptCount >= maxAttempts) + { + throw new Exception("The maximum number of attempts has been reached."); + } + } while (!doneStatusList.Contains(runStatus)); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + + public static async Task RunRunCancelTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Run cancel Testing is starting:", ConsoleColor.Cyan); + var threadResult = await sdk.Beta.Threads.ThreadCreate(); + var threadId = threadResult.Id; + var func = new FunctionDefinitionBuilder("get_corp_location", "get location of corp").AddParameter("name", PropertyDefinition.DefineString("company name, e.g. Betterway")) + .Validate() + .Build(); var assistantResult = await sdk.Beta.Assistants.AssistantCreate(new AssistantCreateRequest + { + Instructions = "You are a professional assistant who provides company information. Company-related data comes from uploaded questions and does not provide vague answers, only clear answers.", + Name = "Qicha", + Tools = new List() { ToolDefinition.DefineCodeInterpreter(), ToolDefinition.DefineRetrieval(), ToolDefinition.DefineFunction(func) }, + Model = Models.Gpt_3_5_Turbo_1106 + }); + var runCreateResult = await sdk.Beta.Runs.RunCreate(threadId, new RunCreateRequest() + { + AssistantId = assistantResult.Id, + }); + + ConsoleExtensions.WriteLine("Run Cancel Test:", ConsoleColor.DarkCyan); + var runResult = await sdk.Beta.Runs.RunCancel(threadId, runCreateResult.Id); + if (runResult.Successful) + { + ConsoleExtensions.WriteLine(runResult.ToJson()); + } + else + { + if (runResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{runResult.Error.Code}: {runResult.Error.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenAI.Playground/TestHelpers/ThreadTestHelper.cs b/OpenAI.Playground/TestHelpers/ThreadTestHelper.cs new file mode 100644 index 00000000..993a0f9f --- /dev/null +++ b/OpenAI.Playground/TestHelpers/ThreadTestHelper.cs @@ -0,0 +1,70 @@ +using OpenAI.Interfaces; +using OpenAI.Playground.ExtensionsAndHelpers; + +namespace OpenAI.Playground.TestHelpers; + +internal static class ThreadTestHelper +{ + public static async Task RunThreadCreateTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Thread create Testing is starting:", ConsoleColor.Cyan); + + try + { + ConsoleExtensions.WriteLine("Thread Create Test:", ConsoleColor.DarkCyan); + var threadResult = await sdk.Beta.Threads.ThreadCreate(); + + if (threadResult.Successful) + { + ConsoleExtensions.WriteLine(threadResult.ToJson()); + return threadResult.Id; + } + else + { + if (threadResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{threadResult.Error.Code}: {threadResult.Error.Message}"); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + throw new InvalidOperationException(); + } + + public static async Task RunThreadRetrieveTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Thread retrieve Testing is starting:", ConsoleColor.Cyan); + try + { + ConsoleExtensions.WriteLine("Thread Retrieve Test:", ConsoleColor.DarkCyan); + var threadId = await RunThreadCreateTest(sdk); + var threadResult = await sdk.Beta.Threads.ThreadRetrieve(threadId); + + if (threadResult.Successful) + { + ConsoleExtensions.WriteLine(threadResult.ToJson()); + } + else + { + if (threadResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + ConsoleExtensions.WriteLine($"{threadResult.Error.Code}: {threadResult.Error.Message}"); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } +} \ No newline at end of file diff --git a/OpenAI.Playground/TestHelpers/TokenizerTestHelper.cs b/OpenAI.Playground/TestHelpers/TokenizerTestHelper.cs index b5e2f378..464730d7 100644 --- a/OpenAI.Playground/TestHelpers/TokenizerTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/TokenizerTestHelper.cs @@ -1,4 +1,5 @@ -using OpenAI.Tokenizer.GPT3; +using OpenAI.Playground.ExtensionsAndHelpers; +using OpenAI.Tokenizer.GPT3; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.Playground/TestHelpers/VisionTestHelper.cs b/OpenAI.Playground/TestHelpers/VisionTestHelper.cs index fc3e09db..2c92a52d 100644 --- a/OpenAI.Playground/TestHelpers/VisionTestHelper.cs +++ b/OpenAI.Playground/TestHelpers/VisionTestHelper.cs @@ -1,6 +1,7 @@ using OpenAI.Interfaces; using OpenAI.ObjectModels; using OpenAI.ObjectModels.RequestModels; +using OpenAI.Playground.ExtensionsAndHelpers; using static OpenAI.ObjectModels.StaticValues; namespace OpenAI.Playground.TestHelpers; diff --git a/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs index be65977d..57b75afe 100644 --- a/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs +++ b/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs @@ -18,32 +18,31 @@ public AzureOpenAiEndpointProvider(string apiVersion, string deploymentId) } private string Prefix => $"{ApiPrefix}/{DeploymentsPrefix}/{WebUtility.UrlEncode(_deploymentId)}"; - private string QueryString => $"?api-version={_apiVersion}"; - + private string AzureVersionQueryString => $"?api-version={_apiVersion}"; public string ModelRetrieve(string model) { - return $"{Prefix}/models/{model}{QueryString}"; + return $"{Prefix}/models/{model}{AzureVersionQueryString}"; } public string FileDelete(string fileId) { - return $"{Prefix}/files/{fileId}{QueryString}"; + return $"{Prefix}/files/{fileId}{AzureVersionQueryString}"; } public string CompletionCreate() { - return $"{Prefix}/completions{QueryString}"; + return $"{Prefix}/completions{AzureVersionQueryString}"; } public string EditCreate() { - return $"{Prefix}/edits{QueryString}"; + return $"{Prefix}/edits{AzureVersionQueryString}"; } public string ModelsList() { - return $"{Prefix}/models{QueryString}"; + return $"{Prefix}/models{AzureVersionQueryString}"; } public string FilesList() @@ -58,47 +57,47 @@ public string FilesUpload() public string FileRetrieve(string fileId) { - return $"{Prefix}/files/{fileId}{QueryString}"; + return $"{Prefix}/files/{fileId}{AzureVersionQueryString}"; } public string FileRetrieveContent(string fileId) { - return $"{Prefix}/files/{fileId}/content{QueryString}"; + return $"{Prefix}/files/{fileId}/content{AzureVersionQueryString}"; } public string FineTuneCreate() { - return $"{Prefix}/fine-tunes{QueryString}"; + return $"{Prefix}/fine-tunes{AzureVersionQueryString}"; } public string FineTuneList() { - return $"{Prefix}/fine-tunes{QueryString}"; + return $"{Prefix}/fine-tunes{AzureVersionQueryString}"; } public string FineTuneRetrieve(string fineTuneId) { - return $"{Prefix}/fine-tunes/{fineTuneId}{QueryString}"; + return $"{Prefix}/fine-tunes/{fineTuneId}{AzureVersionQueryString}"; } public string FineTuneCancel(string fineTuneId) { - return $"{Prefix}/fine-tunes/{fineTuneId}/cancel{QueryString}"; + return $"{Prefix}/fine-tunes/{fineTuneId}/cancel{AzureVersionQueryString}"; } public string FineTuneListEvents(string fineTuneId) { - return $"{Prefix}/fine-tunes/{fineTuneId}/events{QueryString}"; + return $"{Prefix}/fine-tunes/{fineTuneId}/events{AzureVersionQueryString}"; } public string FineTuneDelete(string fineTuneId) { - return $"{Prefix}/models/{fineTuneId}{QueryString}"; + return $"{Prefix}/models/{fineTuneId}{AzureVersionQueryString}"; } public string FineTuningJobCreate() { - return $"{Prefix}/fine_tuning/jobs{QueryString}"; + return $"{Prefix}/fine_tuning/jobs{AzureVersionQueryString}"; } public string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListRequest) @@ -111,85 +110,274 @@ public string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListReque queryParams.Add($"after={WebUtility.UrlEncode(fineTuningJobListRequest.After)}"); if (fineTuningJobListRequest.Limit.HasValue) queryParams.Add($"limit={fineTuningJobListRequest.Limit.Value}"); - + if (queryParams.Any()) - url = $"{url}{QueryString}&{string.Join("&", queryParams)}"; + url = $"{url}{AzureVersionQueryString}&{string.Join("&", queryParams)}"; } - return url; - } - public string FineTuningJobList() - { - return $"{Prefix}/fine_tuning/jobs{QueryString}"; + return url; } public string FineTuningJobRetrieve(string fineTuningJobId) { - return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}{QueryString}"; + return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}{AzureVersionQueryString}"; } public string FineTuningJobCancel(string fineTuningJobId) { - return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/cancel{QueryString}"; + return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/cancel{AzureVersionQueryString}"; } public string FineTuningJobListEvents(string fineTuningJobId) { - return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/events{QueryString}"; + return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/events{AzureVersionQueryString}"; } public string ModelsDelete(string modelId) { - return $"{Prefix}/models/{modelId}{QueryString}"; + return $"{Prefix}/models/{modelId}{AzureVersionQueryString}"; } public string EmbeddingCreate() { - return $"{Prefix}/embeddings{QueryString}"; + return $"{Prefix}/embeddings{AzureVersionQueryString}"; } public string ModerationCreate() { - return $"{Prefix}/moderations{QueryString}"; + return $"{Prefix}/moderations{AzureVersionQueryString}"; } public string ImageCreate() { - return $"{Prefix}/images/generations{QueryString}"; + return $"{Prefix}/images/generations{AzureVersionQueryString}"; } public string ImageEditCreate() { - return $"{Prefix}/images/edits{QueryString}"; + return $"{Prefix}/images/edits{AzureVersionQueryString}"; } public string ImageVariationCreate() { - return $"{Prefix}/images/variations{QueryString}"; + return $"{Prefix}/images/variations{AzureVersionQueryString}"; } public string ChatCompletionCreate() { - return $"{Prefix}/chat/completions{QueryString}"; + return $"{Prefix}/chat/completions{AzureVersionQueryString}"; } public string AudioCreateTranscription() { - return $"{Prefix}/audio/transcriptions{QueryString}"; + return $"{Prefix}/audio/transcriptions{AzureVersionQueryString}"; } public string AudioCreateTranslation() { - return $"{Prefix}/audio/translation{QueryString}"; + return $"{Prefix}/audio/translation{AzureVersionQueryString}"; } public string AudioCreateSpeech() { - return $"{Prefix}/audio/speech{QueryString}"; + return $"{Prefix}/audio/speech{AzureVersionQueryString}"; + } + + public string AssistantCreate() + { + return $"{Prefix}/assistants{AzureVersionQueryString}"; + } + + public string AssistantRetrieve(string assistantId) + { + return $"{Prefix}/assistants/{assistantId}{AzureVersionQueryString}"; + } + + public string AssistantModify(string assistantId) + { + return $"{Prefix}/assistants/{assistantId}{AzureVersionQueryString}"; + } + + public string AssistantDelete(string assistantId) + { + return $"{Prefix}/assistants/{assistantId}{AzureVersionQueryString}"; + } + + public string AssistantList(AssistantListRequest? assistantListRequest) + { + var url = $"{Prefix}/assistants{AzureVersionQueryString}"; + + var query = assistantListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string AssistantFileCreate(string assistantId) + { + return $"{Prefix}/assistants/{assistantId}/files{AzureVersionQueryString}"; + } + + public string AssistantFileRetrieve(string assistantId, string fileId) + { + return $"{Prefix}/assistants/{assistantId}/files/{fileId}{AzureVersionQueryString}"; + } + + public string AssistantFileDelete(string assistantId, string fileId) + { + return $"{Prefix}/assistants/{assistantId}/files/{fileId}{AzureVersionQueryString}"; + } + + public string AssistantFileList(string assistantId, AssistantFileListRequest? assistantFileListRequest) + { + var url = $"{Prefix}/assistants/files{AzureVersionQueryString}"; + + var query = assistantFileListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string ThreadCreate() + { + return $"{Prefix}/threads{AzureVersionQueryString}"; + } + + public string ThreadRetrieve(string threadId) + { + return $"{Prefix}/threads/{threadId}{AzureVersionQueryString}"; + } + + public string ThreadModify(string threadId) + { + return $"{Prefix}/threads/{threadId}{AzureVersionQueryString}"; + } + + public string ThreadDelete(string threadId) + { + return $"{Prefix}/threads/{threadId}{AzureVersionQueryString}"; + } + + public string MessageCreate(string threadId) + { + return $"{Prefix}/threads/{threadId}/messages{AzureVersionQueryString}"; + } + + public string MessageRetrieve(string threadId, string messageId) + { + return $"{Prefix}/threads/{threadId}/messages/{messageId}{AzureVersionQueryString}"; + } + + public string MessageModify(string threadId, string messageId) + { + return $"{Prefix}/threads/{threadId}/messages/{messageId}{AzureVersionQueryString}"; + } + + public string MessageList(string threadId, MessageListRequest? messageListRequest) + { + var url = $"{Prefix}/threads/{threadId}/messages{AzureVersionQueryString}"; + + var query = messageListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string MessageFileRetrieve(string threadId, string messageId, string fileId) + { + return $"{Prefix}/threads/{threadId}/messages/{messageId}/files/{fileId}{AzureVersionQueryString}"; + } + + public string MessageFileList(string threadId, string messageId, MessageFileListRequest? messageFileListRequest) + { + var url = $"{Prefix}/threads/{threadId}/messages/{messageId}/files{AzureVersionQueryString}"; + + var query = messageFileListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string RunCreate(string threadId) + { + return $"{Prefix}/threads/{threadId}/runs{AzureVersionQueryString}"; + } + + public string RunRetrieve(string threadId, string runId) + { + return $"{Prefix}/threads/{threadId}/runs/{runId}{AzureVersionQueryString}"; + } + + public string RunModify(string threadId, string runId) + { + return $"{Prefix}/threads/{threadId}/runs/{runId}{AzureVersionQueryString}"; + } + + public string RunList(string threadId, RunListRequest? runListRequest) + { + var url = $"{Prefix}/threads/{threadId}/runs{AzureVersionQueryString}"; + + var query = runListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string RunSubmitToolOutputs(string threadId, string runId) + { + return $"{Prefix}/threads/{threadId}/runs/{runId}/submit_tool_outputs{AzureVersionQueryString}"; + } + + public string RunCancel(string threadId, string runId) + { + return $"{Prefix}/threads/{threadId}/runs/{runId}/cancel{AzureVersionQueryString}"; + } + + public string ThreadAndRunCreate() + { + return $"{Prefix}/threads/runs{AzureVersionQueryString}"; + } + + public string RunStepRetrieve(string threadId, string runId, string stepId) + { + return $"{Prefix}/threads/{threadId}/runs/{runId}/steps/{stepId}{AzureVersionQueryString}"; + } + + public string RunStepList(string threadId, string runId, RunStepListRequest? runStepListRequest) + { + var url = $"{Prefix}/threads/{threadId}/runs/{runId}/steps{AzureVersionQueryString}"; + + var query = runStepListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}{query}"; + } + + return url; + } + + public string FineTuningJobList() + { + return $"{Prefix}/fine_tuning/jobs{AzureVersionQueryString}"; } private string Files() { - return $"{Prefix}/files{QueryString}"; + return $"{Prefix}/files{AzureVersionQueryString}"; } } \ No newline at end of file diff --git a/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs index 8429637e..9b69f952 100644 --- a/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs +++ b/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs @@ -34,4 +34,33 @@ internal interface IOpenAiEndpointProvider string AudioCreateTranscription(); string AudioCreateTranslation(); string AudioCreateSpeech(); + + string AssistantCreate(); + string AssistantRetrieve(string assistantId); + string AssistantModify(string assistantId); + string AssistantDelete(string assistantId); + string AssistantList(AssistantListRequest? assistantListRequest); + string AssistantFileCreate(string assistantId); + string AssistantFileRetrieve(string assistantId, string fileId); + string AssistantFileDelete(string assistantId, string fileId); + string AssistantFileList(string assistantId, AssistantFileListRequest? assistantFileListRequest); + string ThreadCreate(); + string ThreadRetrieve(string threadId); + string ThreadModify(string threadId); + string ThreadDelete(string threadId); + string MessageCreate(string threadId); + string MessageRetrieve(string threadId,string messageId); + string MessageModify(string threadId, string messageId); + string MessageList(string threadId, MessageListRequest? messageListRequest); + string MessageFileRetrieve(string threadId, string messageId, string fileId); + string MessageFileList(string threadId, string messageId, MessageFileListRequest? messageFileListRequest); + string RunCreate(string threadId); + string RunRetrieve(string threadId, string runId); + string RunModify(string threadId, string runId); + string RunList(string threadId, RunListRequest? runListRequest); + string RunSubmitToolOutputs(string threadId, string runId); + string RunCancel(string threadId, string runId); + string ThreadAndRunCreate(); + string RunStepRetrieve(string threadId, string runId,string stepId); + string RunStepList(string threadId, string runId, RunStepListRequest? runStepListRequest); } \ No newline at end of file diff --git a/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs index 61692008..936c4745 100644 --- a/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs +++ b/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs @@ -59,12 +59,12 @@ public string ModelsList() public string FilesList() { - return Files(); + return $"{_apiVersion}/files"; } public string FilesUpload() { - return Files(); + return $"{_apiVersion}/files"; } public string FileRetrieve(string fileId) @@ -122,10 +122,11 @@ public string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListReque queryParams.Add($"after={WebUtility.UrlEncode(fineTuningJobListRequest.After)}"); if (fineTuningJobListRequest.Limit.HasValue) queryParams.Add($"limit={fineTuningJobListRequest.Limit.Value}"); - + if (queryParams.Any()) url = $"{url}?{string.Join("&", queryParams)}"; } + return url; } @@ -174,8 +175,191 @@ public string ImageVariationCreate() return $"{_apiVersion}/images/variations"; } - private string Files() + public string AssistantCreate() { - return $"{_apiVersion}/files"; + return $"{_apiVersion}/assistants"; + } + + public string AssistantRetrieve(string assistantId) + { + return $"{_apiVersion}/assistants/{assistantId}"; + } + + public string AssistantModify(string assistantId) + { + return $"{_apiVersion}/assistants/{assistantId}"; + } + + public string AssistantDelete(string assistantId) + { + return $"{_apiVersion}/assistants/{assistantId}"; + } + + public string AssistantList(AssistantListRequest? assistantListRequest) + { + var url = $"{_apiVersion}/assistants"; + + var query = assistantListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; + } + + public string AssistantFileCreate(string assistantId) + { + return $"{_apiVersion}/assistants/{assistantId}/files"; + } + + public string AssistantFileRetrieve(string assistantId, string fileId) + { + return $"{_apiVersion}/assistants/{assistantId}/files/{fileId}"; + } + + public string AssistantFileDelete(string assistantId, string fileId) + { + return $"{_apiVersion}/assistants/{assistantId}/files/{fileId}"; + } + + public string AssistantFileList(string assistantId, AssistantFileListRequest? assistantFileListRequest) + { + var url = $"{_apiVersion}/assistants/{assistantId}/files"; + + var query = assistantFileListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; + } + + public string ThreadCreate() + { + return $"{_apiVersion}/threads"; + } + + public string ThreadRetrieve(string threadId) + { + return $"{_apiVersion}/threads/{threadId}"; + } + + public string ThreadModify(string threadId) + { + return $"{_apiVersion}/threads/{threadId}"; + } + + public string ThreadDelete(string threadId) + { + return $"{_apiVersion}/threads/{threadId}"; + } + + public string MessageCreate(string threadId) + { + return $"{_apiVersion}/threads/{threadId}/messages"; + } + + public string MessageRetrieve(string threadId, string messageId) + { + return $"{_apiVersion}/threads/{threadId}/messages/{messageId}"; + } + + public string MessageModify(string threadId, string messageId) + { + return $"{_apiVersion}/threads/{threadId}/messages/{messageId}"; + } + + public string MessageList(string threadId, MessageListRequest? messageListRequest) + { + var url = $"{_apiVersion}/threads/{threadId}/messages"; + + var query = messageListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; + } + + public string MessageFileRetrieve(string threadId, string messageId, string fileId) + { + return $"{_apiVersion}/threads/{threadId}/messages/{messageId}/files/{fileId}"; + } + + public string MessageFileList(string threadId, string messageId, MessageFileListRequest? messageFileListRequest) + { + var url = $"{_apiVersion}/threads/{threadId}/messages/{messageId}/files"; + + var query = messageFileListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; + } + + public string RunCreate(string threadId) + { + return $"{_apiVersion}/threads/{threadId}/runs"; + } + + public string RunRetrieve(string threadId, string runId) + { + return $"{_apiVersion}/threads/{threadId}/runs/{runId}"; + } + + public string RunModify(string threadId, string runId) + { + return $"{_apiVersion}/threads/{threadId}/runs/{runId}"; + } + + public string RunList(string threadId, RunListRequest? runListRequest) + { + var url = $"{_apiVersion}/threads/{threadId}/runs"; + + var query = runListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; + } + + public string RunSubmitToolOutputs(string threadId, string runId) + { + return $"{_apiVersion}/threads/{threadId}/runs/{runId}/submit_tool_outputs"; + } + + public string RunCancel(string threadId, string runId) + { + return $"{_apiVersion}/threads/{threadId}/runs/{runId}/cancel"; + } + + public string ThreadAndRunCreate() + { + return $"{_apiVersion}/threads/runs"; + } + + public string RunStepRetrieve(string threadId, string runId, string stepId) + { + return $"{_apiVersion}/threads/{threadId}/runs/{runId}/steps/{stepId}"; + } + + public string RunStepList(string threadId, string runId, RunStepListRequest? runStepListRequest) + { + var url = $"{_apiVersion}/threads/{threadId}/runs/{runId}/steps"; + + var query = runStepListRequest?.GetQueryParameters(); + if (!string.IsNullOrWhiteSpace(query)) + { + url = $"{url}?{query}"; + } + + return url; } } \ No newline at end of file diff --git a/OpenAI.SDK/Extensions/HttpclientExtensions.cs b/OpenAI.SDK/Extensions/HttpclientExtensions.cs index 9fbe37ff..6bcc4490 100644 --- a/OpenAI.SDK/Extensions/HttpclientExtensions.cs +++ b/OpenAI.SDK/Extensions/HttpclientExtensions.cs @@ -8,6 +8,12 @@ namespace OpenAI.Extensions; internal static class HttpClientExtensions { + public static async Task GetReadAsAsync(this HttpClient client, string uri, CancellationToken cancellationToken = default) where TResponse : BaseResponse, new() + { + var response = await client.GetAsync(uri, cancellationToken); + return await HandleResponseContent(response, cancellationToken); + } + public static async Task PostAndReadAsAsync(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default) where TResponse : BaseResponse, new() { var response = await client.PostAsJsonAsync(uri, requestModel, new JsonSerializerOptions diff --git a/OpenAI.SDK/Extensions/ModelExtension.cs b/OpenAI.SDK/Extensions/ModelExtension.cs index dce014ae..e2df1048 100644 --- a/OpenAI.SDK/Extensions/ModelExtension.cs +++ b/OpenAI.SDK/Extensions/ModelExtension.cs @@ -4,8 +4,15 @@ namespace OpenAI.Extensions; public static class ModelExtension { - public static void ProcessModelId(this IOpenAiModels.IModel modelFromObject, string? modelFromParameter, string? defaultModelId) + public static void ProcessModelId(this IOpenAiModels.IModel modelFromObject, string? modelFromParameter, string? defaultModelId,bool allowNull =false) { - modelFromObject.Model = modelFromParameter ?? modelFromObject.Model ?? defaultModelId ?? throw new ArgumentNullException("Model Id"); + if (allowNull) + { + modelFromObject.Model = modelFromParameter ?? modelFromObject.Model ?? defaultModelId; + } + else + { + modelFromObject.Model = modelFromParameter ?? modelFromObject.Model ?? defaultModelId ?? throw new ArgumentNullException("Model Id"); + } } } \ No newline at end of file diff --git a/OpenAI.SDK/Extensions/OpenAIServiceCollectionExtensions.cs b/OpenAI.SDK/Extensions/OpenAIServiceCollectionExtensions.cs index efc4fdf2..b4d1e4f8 100644 --- a/OpenAI.SDK/Extensions/OpenAIServiceCollectionExtensions.cs +++ b/OpenAI.SDK/Extensions/OpenAIServiceCollectionExtensions.cs @@ -9,30 +9,23 @@ public static class OpenAIServiceCollectionExtensions public static IHttpClientBuilder AddOpenAIService(this IServiceCollection services, Action? setupAction = null) { var optionsBuilder = services.AddOptions(); + optionsBuilder.BindConfiguration(OpenAiOptions.SettingKey); if (setupAction != null) { optionsBuilder.Configure(setupAction); } - else - { - optionsBuilder.BindConfiguration(OpenAiOptions.SettingKey); - } return services.AddHttpClient(); } - public static IHttpClientBuilder AddOpenAIService(this IServiceCollection services, string name, Action? setupAction = null) - where TServiceInterface : class, IOpenAIService + public static IHttpClientBuilder AddOpenAIService(this IServiceCollection services, string name, Action? setupAction = null) where TServiceInterface : class, IOpenAIService { var optionsBuilder = services.AddOptions(name); + optionsBuilder.BindConfiguration($"{OpenAiOptions.SettingKey}:{name}"); if (setupAction != null) { optionsBuilder.Configure(setupAction); } - else - { - optionsBuilder.BindConfiguration($"{OpenAiOptions.SettingKey}:{name}"); - } return services.AddHttpClient(); } diff --git a/OpenAI.SDK/Interfaces/IAssistantService.cs b/OpenAI.SDK/Interfaces/IAssistantService.cs new file mode 100644 index 00000000..4b88d19a --- /dev/null +++ b/OpenAI.SDK/Interfaces/IAssistantService.cs @@ -0,0 +1,86 @@ +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Interfaces; + +public interface IAssistantService +{ + /// + /// Create an assistant with a model and instructions. + /// + /// + /// + /// + /// + Task AssistantCreate(AssistantCreateRequest request, string? modelId = null, CancellationToken cancellationToken = default); + + /// + /// Create an assistant file by attaching a File to an assistant. + /// + /// + /// + /// + /// + Task AssistantFileCreate(string assistantId, AssistantFileCreateRequest request, CancellationToken cancellationToken = default); + + /// + /// Returns a list of assistants. + /// + /// + /// + /// + Task AssistantList(AssistantListRequest? request = null, CancellationToken cancellationToken = default); + + /// + /// Returns a list of assistant files. + /// + /// + /// + /// + /// + Task AssistantFileList(string assistantId, AssistantFileListRequest? request = null, CancellationToken cancellationToken = default); + + /// + /// Retrieves an assistant. + /// + /// + /// + /// + Task AssistantRetrieve(string assistantId, CancellationToken cancellationToken = default); + + /// + /// Retrieves an AssistantFile. + /// + /// + /// + /// + /// + Task AssistantFileRetrieve(string assistantId, string fileId, CancellationToken cancellationToken = default); + + /// + /// Modifies an assistant. + /// + /// + /// + /// + /// + Task AssistantModify(string assistantId, AssistantModifyRequest request, CancellationToken cancellationToken = default); + + /// + /// Delete an assistant. + /// + /// + /// + /// + Task AssistantDelete(string assistantId, CancellationToken cancellationToken = default); + + /// + /// Delete an assistant file. + /// + /// + /// + /// + /// + Task AssistantFileDelete(string assistantId, string fileId, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IBetaService.cs b/OpenAI.SDK/Interfaces/IBetaService.cs new file mode 100644 index 00000000..3972503c --- /dev/null +++ b/OpenAI.SDK/Interfaces/IBetaService.cs @@ -0,0 +1,12 @@ +namespace OpenAI.Interfaces; + +public interface IBetaService +{ + public IAssistantService Assistants { get; } + + public IMessageService Messages { get; } + + public IThreadService Threads { get; } + + public IRunService Runs { get; } +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IMessageService.cs b/OpenAI.SDK/Interfaces/IMessageService.cs new file mode 100644 index 00000000..d9335c93 --- /dev/null +++ b/OpenAI.SDK/Interfaces/IMessageService.cs @@ -0,0 +1,26 @@ +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Interfaces; + +public interface IMessageService +{ + /// + /// Create a message. + /// + /// + /// + /// + /// + Task MessageCreate(string threadId, MessageCreateRequest request, CancellationToken cancellationToken = default); + + /// + /// Returns a list of messages for a given thread. + /// + /// + /// + /// + /// + Task MessageList(string threadId, MessageListRequest? request = null, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IOpenAIService.cs b/OpenAI.SDK/Interfaces/IOpenAIService.cs index 671865d4..4987c116 100644 --- a/OpenAI.SDK/Interfaces/IOpenAIService.cs +++ b/OpenAI.SDK/Interfaces/IOpenAIService.cs @@ -55,6 +55,10 @@ public interface IOpenAIService /// public IAudioService Audio { get; } + /// + /// Beta + /// + public IBetaService Beta { get; } /// /// Set default model diff --git a/OpenAI.SDK/Interfaces/IRunService.cs b/OpenAI.SDK/Interfaces/IRunService.cs new file mode 100644 index 00000000..221deed8 --- /dev/null +++ b/OpenAI.SDK/Interfaces/IRunService.cs @@ -0,0 +1,50 @@ +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Interfaces; + +public interface IRunService +{ + /// + /// Create a run. + /// + /// + /// + /// + /// + /// + Task RunCreate(string threadId, RunCreateRequest request, string? modelId = null, CancellationToken cancellationToken = default); + + /// + /// Retrieves a run. + /// + /// + /// + /// + /// + Task RunRetrieve(string threadId, string runId, CancellationToken cancellationToken = default); + + /// + /// Cancels a run that is in_progress. + /// + /// + /// + /// + /// + Task RunCancel(string threadId, string runId, CancellationToken cancellationToken = default); + + /// + /// Submit tool outputs to run + /// + /// When a run has the status: "requires_action" and required_action.type is submit_tool_outputs, + /// this endpoint can be used to submit the outputs from the tool calls once they're all completed. + /// All outputs must be submitted in a single request. + /// + /// + /// + /// + /// + /// + /// + Task RunSubmitToolOutputs(string threadId, string runId, SubmitToolOutputsToRunRequest request, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IThreadService.cs b/OpenAI.SDK/Interfaces/IThreadService.cs new file mode 100644 index 00000000..f489ac33 --- /dev/null +++ b/OpenAI.SDK/Interfaces/IThreadService.cs @@ -0,0 +1,31 @@ +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Interfaces; + +public interface IThreadService +{ + /// + /// Create a thread. + /// + /// + /// + /// + Task ThreadCreate(ThreadCreateRequest? request = null, CancellationToken cancellationToken = default); + + /// + /// Retrieves a thread. + /// + /// + /// + /// + Task ThreadRetrieve(string threadId, CancellationToken cancellationToken = default); + + /// + /// Delete a thread. + /// + /// + /// + /// + Task ThreadDelete(string threadId, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIAssistantService.cs b/OpenAI.SDK/Managers/OpenAIAssistantService.cs new file mode 100644 index 00000000..f9145185 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIAssistantService.cs @@ -0,0 +1,105 @@ +using OpenAI.Extensions; +using OpenAI.Interfaces; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Managers; + +public partial class OpenAIService : IAssistantService +{ + /// + public async Task AssistantCreate(AssistantCreateRequest request, string? modelId = null, CancellationToken cancellationToken = default) + { + request.ProcessModelId(modelId, _defaultModelId); + return await _httpClient.PostAndReadAsAsync(_endpointProvider.AssistantCreate(), request, cancellationToken); + } + + /// + public async Task AssistantFileCreate(string assistantId, AssistantFileCreateRequest request, CancellationToken cancellationToken = default) + { + return await _httpClient.PostAndReadAsAsync(_endpointProvider.AssistantFileCreate(assistantId), request, cancellationToken); + } + + /// + public async Task AssistantList(AssistantListRequest? request = null, CancellationToken cancellationToken = default) + { + return await _httpClient.GetReadAsAsync(_endpointProvider.AssistantList(request), cancellationToken); + } + + /// + public async Task AssistantFileList(string assistantId, AssistantFileListRequest? request = null, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.AssistantFileList(assistantId, request), cancellationToken); + } + + /// + public async Task AssistantRetrieve(string assistantId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.AssistantRetrieve(assistantId), cancellationToken); + } + + /// + public async Task AssistantFileRetrieve(string assistantId, string fileId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + if (string.IsNullOrWhiteSpace(fileId)) + { + throw new ArgumentNullException(nameof(fileId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.AssistantFileRetrieve(assistantId, fileId), cancellationToken); + } + + /// + public async Task AssistantModify(string assistantId, AssistantModifyRequest request, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + return await _httpClient.PostAndReadAsAsync(_endpointProvider.AssistantModify(assistantId), request, cancellationToken); + } + + /// + public async Task AssistantDelete(string assistantId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + return await _httpClient.DeleteAndReadAsAsync(_endpointProvider.AssistantDelete(assistantId), cancellationToken); + } + + /// + public async Task AssistantFileDelete(string assistantId, string fileId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(assistantId)) + { + throw new ArgumentNullException(nameof(assistantId)); + } + + if (string.IsNullOrWhiteSpace(fileId)) + { + throw new ArgumentNullException(nameof(fileId)); + } + + return await _httpClient.DeleteAndReadAsAsync(_endpointProvider.AssistantFileDelete(assistantId, fileId), cancellationToken); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIBetaService.cs b/OpenAI.SDK/Managers/OpenAIBetaService.cs new file mode 100644 index 00000000..e5ef5876 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIBetaService.cs @@ -0,0 +1,17 @@ +using OpenAI.Interfaces; + +namespace OpenAI.Managers; + +/// +/// Beta service for OpenAI. +/// +public partial class OpenAIService : IBetaService +{ + public IAssistantService Assistants => this; + + public IMessageService Messages => this; + + public IThreadService Threads => this; + + public IRunService Runs => this; +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIMessageService.cs b/OpenAI.SDK/Managers/OpenAIMessageService.cs new file mode 100644 index 00000000..b12458cd --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIMessageService.cs @@ -0,0 +1,46 @@ +using OpenAI.Extensions; +using OpenAI.Interfaces; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Managers; + +public partial class OpenAIService : IMessageService +{ + /// + /// Create a message. + /// + /// + /// + /// + /// + /// + public async Task MessageCreate(string threadId, MessageCreateRequest request, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + return await _httpClient.PostAndReadAsAsync(_endpointProvider.MessageCreate(threadId), request, cancellationToken); + } + + /// + /// Returns a list of messages for a given thread. + /// + /// + /// + /// + /// + /// + public async Task MessageList(string threadId, MessageListRequest? request = null, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.MessageList(threadId, request), cancellationToken); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIRunService.cs b/OpenAI.SDK/Managers/OpenAIRunService.cs new file mode 100644 index 00000000..c2b7a2d9 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIRunService.cs @@ -0,0 +1,99 @@ +using OpenAI.Extensions; +using OpenAI.Interfaces; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Managers; + +public partial class OpenAIService : IRunService +{ + /// + /// Create a run. + /// + /// + /// + /// + /// + /// + /// + public async Task RunCreate(string threadId, RunCreateRequest request, string? modelId = null, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + request.ProcessModelId(modelId, _defaultModelId,true); + return await _httpClient.PostAndReadAsAsync(_endpointProvider.RunCreate(threadId), request, cancellationToken); + } + + /// + /// Retrieves a run. + /// + /// + /// + /// + /// + /// + public async Task RunRetrieve(string threadId, string runId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + if (string.IsNullOrWhiteSpace(runId)) + { + throw new ArgumentNullException(nameof(runId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.RunRetrieve(threadId, runId), cancellationToken); + } + + /// + /// Cancels a run that is in_progress. + /// + /// + /// + /// + /// + /// + public async Task RunCancel(string threadId, string runId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + return await _httpClient.PostAndReadAsAsync(_endpointProvider.RunCancel(threadId, runId), null, cancellationToken); + } + + /// + /// Submit tool outputs to run + /// + /// When a run has the status: "requires_action" and required_action.type is submit_tool_outputs, + /// this endpoint can be used to submit the outputs from the tool calls once they're all completed. + /// All outputs must be submitted in a single request. + /// + /// + /// + /// + /// + /// + /// + /// + public async Task RunSubmitToolOutputs(string threadId, string runId, SubmitToolOutputsToRunRequest request, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + if (string.IsNullOrWhiteSpace(runId)) + { + throw new ArgumentNullException(nameof(runId)); + } + + return await _httpClient.PostAndReadAsAsync(_endpointProvider.RunSubmitToolOutputs(threadId, runId), request, cancellationToken); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIService.cs b/OpenAI.SDK/Managers/OpenAIService.cs index 7ed568ae..dca33a6a 100644 --- a/OpenAI.SDK/Managers/OpenAIService.cs +++ b/OpenAI.SDK/Managers/OpenAIService.cs @@ -42,6 +42,10 @@ public OpenAIService(OpenAiOptions settings, HttpClient? httpClient = null) case ProviderType.OpenAi: default: _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {settings.ApiKey}"); + if (settings.UseBeta) + { + _httpClient.DefaultRequestHeaders.Add("OpenAI-Beta", settings.Assistants); + } break; } @@ -102,6 +106,9 @@ public void Dispose() /// public IAudioService Audio => this; + /// + public IBetaService Beta => this; + /// /// Sets default Model Id /// diff --git a/OpenAI.SDK/Managers/OpenAIThreadService.cs b/OpenAI.SDK/Managers/OpenAIThreadService.cs new file mode 100644 index 00000000..3f869417 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIThreadService.cs @@ -0,0 +1,55 @@ +using OpenAI.Extensions; +using OpenAI.Interfaces; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Managers; + +public partial class OpenAIService : IThreadService +{ + /// + /// Create a thread. + /// + /// + /// + /// + /// + public async Task ThreadCreate(ThreadCreateRequest? request = null, CancellationToken cancellationToken = default) + { + return await _httpClient.PostAndReadAsAsync(_endpointProvider.ThreadCreate(), request, cancellationToken); + } + + /// + /// Retrieves a thread. + /// + /// + /// + /// + /// + public async Task ThreadRetrieve(string threadId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + return await _httpClient.GetReadAsAsync(_endpointProvider.ThreadRetrieve(threadId), cancellationToken); + } + + /// + /// Delete a thread. + /// + /// + /// + /// + /// + public async Task ThreadDelete(string threadId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(threadId)) + { + throw new ArgumentNullException(nameof(threadId)); + } + + return await _httpClient.DeleteAndReadAsAsync(_endpointProvider.ThreadRetrieve(threadId), cancellationToken); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/AssistantCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/AssistantCreateRequest.cs new file mode 100644 index 00000000..355c7ea5 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/AssistantCreateRequest.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.RequestModels; + +public class AssistantCreateRequest : IOpenAiModels.IModel, IOpenAiModels.IFileIds, IOpenAiModels.IMetaData +{ + /// + /// The name of the assistant. The maximum length is 256 + /// + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// + /// The description of the assistant. + /// + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// The system instructions that the assistant uses. + /// + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } + + /// + /// A list of tools enabled on the assistant. + /// + [JsonPropertyName("tools")] + public List? Tools { get; set; } + + /// + /// A list of file IDs attached to this assistant. + /// + [JsonPropertyName("file_ids")] + public List? FileIds { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } + + /// + /// ID of the model to use + /// + [JsonPropertyName("model")] + public string Model { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileCreateRequest.cs new file mode 100644 index 00000000..0c90874e --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileCreateRequest.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.RequestModels; + +public class AssistantFileCreateRequest +{ + /// + /// A File ID (with purpose="assistants") that the assistant should use. Useful for tools like retrieval and + /// code_interpreter that can access files. + /// + [JsonPropertyName("file_id")] + public string FileId { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileListRequest.cs new file mode 100644 index 00000000..2c9601a4 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/AssistantFileListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class AssistantFileListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/AssistantListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/AssistantListRequest.cs new file mode 100644 index 00000000..92197071 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/AssistantListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class AssistantListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/AssistantModifyRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/AssistantModifyRequest.cs new file mode 100644 index 00000000..ba3be9e5 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/AssistantModifyRequest.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.RequestModels; + +public class AssistantModifyRequest : IOpenAiModels.IModel, IOpenAiModels.IFileIds, IOpenAiModels.IMetaData +{ + /// + /// The name of the assistant. The maximum length is 256 + /// + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// + /// The description of the assistant. The maximum length is 512 characters. + /// + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// The system instructions that the assistant uses. The maximum length is 32768 characters. + /// + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } + + /// + /// A list of tools enabled on the assistant. + /// + [JsonPropertyName("tools")] + public List? Tools { get; set; } + + /// + /// A list of File IDs attached to this assistant. + /// + [JsonPropertyName("file_ids")] + public List? FileIds { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } + + /// + /// ID of the model to use + /// + [JsonPropertyName("model")] + public string Model { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/BaseListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/BaseListRequest.cs new file mode 100644 index 00000000..1307dfd6 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/BaseListRequest.cs @@ -0,0 +1,66 @@ +using System.Net; +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.RequestModels; + +public class BaseListRequest +{ + /// + /// A limit on the number of objects to be returned. + /// Limit can range between 1 and 100, and the default is 20. + /// + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + /// + /// Sort order by the created_at timestamp of the objects. + /// "asc" for ascending order and "desc" for descending order. + /// + [JsonPropertyName("order")] + public string? Order { get; set; } + + + /// + /// A cursor for use in pagination. after is an object ID that defines your place in the list. + /// For instance, if you make a list request and receive 100 objects, ending with obj_foo, + /// your subsequent call can include after=obj_foo in order to fetch the next page of the list. + /// + [JsonPropertyName("after")] + public string? After { get; set; } + + /// + /// A cursor for use in pagination. before is an object ID that defines your place in the list. + /// For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + /// subsequent call can include before=obj_foo in order to fetch the previous page of the list. + /// + [JsonPropertyName("before")] + public string? Before { get; set; } + + public string? GetQueryParameters() + { + var build = new List(); + if (Limit != null) + { + build.Add($"limit={Limit}"); + } + + if (Order != null) + { + build.Add($"order={WebUtility.UrlEncode(Order)}"); + } + + if (After != null) + { + build.Add($"after={WebUtility.UrlEncode(After)}"); + } + + if (Before != null) + { + build.Add($"before={WebUtility.UrlEncode(Before)}"); + } + + if (build.Count <= 0) return null; + + return string.Join("&", build); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/MessageCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/MessageCreateRequest.cs new file mode 100644 index 00000000..37be5e22 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/MessageCreateRequest.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.RequestModels; + +public class MessageCreateRequest : IOpenAiModels.IFileIds, IOpenAiModels.IMetaData +{ + /// + /// The role of the entity that is creating the message. + /// Currently only user is supported. + /// + [JsonPropertyName("role")] + public string Role { get; set; } = StaticValues.AssistantsStatics.MessageStatics.Roles.User; + + /// + /// The content of the message. + /// + [JsonPropertyName("content")] + public string Content { get; set; } + + /// + /// A list of File IDs that the message should use. + /// There can be a maximum of 10 files attached to a message. + /// Useful for tools like retrieval and code_interpreter that can access and use files. + /// + [JsonPropertyName("file_ids")] + public List? FileIds { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about the object in a structured format. + /// Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/MessageFileListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/MessageFileListRequest.cs new file mode 100644 index 00000000..98b26af9 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/MessageFileListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class MessageFileListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/MessageListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/MessageListRequest.cs new file mode 100644 index 00000000..a2ef4b36 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/MessageListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class MessageListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/RunCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/RunCreateRequest.cs new file mode 100644 index 00000000..a6b7f974 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/RunCreateRequest.cs @@ -0,0 +1,108 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.RequestModels; + +public class RunCreateRequest : IOpenAiModels.IModel, IOpenAiModels.IMetaData, IOpenAiModels.ITemperature +{ + /// + /// The ID of the assistant to use to execute this run. + /// + [JsonPropertyName("assistant_id")] + public string AssistantId { get; set; } + + /// + /// Overrides the instructions of the assistant. + /// This is useful for modifying the behavior on a per-run basis. + /// + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } + + /// + /// Appends additional instructions at the end of the instructions for the run. + /// This is useful for modifying the behavior on a per-run basis without overriding other instructions. + /// + [JsonPropertyName("additional_instructions")] + public string? AdditionalInstructions { get; set; } + + /// + /// Adds additional messages to the thread before creating the run. + /// + [JsonPropertyName("additional_messages")] + public List? AdditionalMessages { get; set; } + + /// + /// Override the tools the assistant can use for this run. + /// This is useful for modifying the behavior on a per-run basis. + /// + [JsonPropertyName("tools")] + public List? Tools { get; set; } + + /// + /// If true, returns a stream of events that happen during the Run as server-sent events, + /// terminating when the Run enters a terminal state with a data: [DONE] message. + /// + [JsonPropertyName("stream")] + public bool? Stream { get; set; } + + /// + /// The maximum number of prompt tokens that may be used over the course of the run. + /// The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the + /// run. + /// If the run exceeds the number of prompt tokens specified, the run will end with status complete. + /// See incomplete_details for more info. + /// + [JsonPropertyName("max_prompt_tokens")] + public int? MaxPromptTokens { get; set; } + + /// + /// The maximum number of completion tokens that may be used over the course of the run. + /// The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the + /// run. + /// If the run exceeds the number of completion tokens specified, the run will end with status complete. + /// See incomplete_details for more info. + /// + [JsonPropertyName("max_completion_tokens")] + public int? MaxCompletionTokens { get; set; } + + /// + /// The truncation strategy to use for the thread. + /// + [JsonPropertyName("truncation_strategy")] + public TruncationStrategy? TruncationStrategy { get; set; } + + /// + /// Controls which (if any) tool is called by the model. + /// + [JsonPropertyName("tool_choice")] + public ToolChoice? ToolChoice { get; set; } + + /// + /// Specifies the format that the model must output. + /// + [JsonPropertyName("response_format")] + public ResponseFormat? ResponseFormat { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about the object in a structured format. + /// Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } + + /// + /// The ID of the Model to be used to execute this run. + /// If a value is provided here, it will override the model associated with the assistant. + /// If not, the model associated with the assistant will be used. + /// + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// + /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, + /// while lower values like 0.2 will make it more focused and deterministic. Defaults to 1. + /// + [JsonPropertyName("temperature")] + public float? Temperature { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/RunListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/RunListRequest.cs new file mode 100644 index 00000000..f81db89c --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/RunListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class RunListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/RunStepListRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/RunStepListRequest.cs new file mode 100644 index 00000000..f4c690c6 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/RunStepListRequest.cs @@ -0,0 +1,5 @@ +namespace OpenAI.ObjectModels.RequestModels; + +public class RunStepListRequest : BaseListRequest +{ +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/SubmitToolOutputsToRunRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/SubmitToolOutputsToRunRequest.cs new file mode 100644 index 00000000..6e5be108 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/SubmitToolOutputsToRunRequest.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.RequestModels; + +public class SubmitToolOutputsToRunRequest +{ + /// + /// A list of tools for which the outputs are being submitted. + /// + [Required] + [JsonPropertyName("tool_outputs")] + public List ToolOutputs { get; set; } +} + +/// +/// A list of tools for which the outputs are being submitted. +/// +public class ToolOutput +{ + /// + /// The ID of the tool call in the required_action object + /// within the run object the output is being submitted for. + /// + [JsonPropertyName("tool_call_id")] + public string? ToolCallId { get; set; } + + /// + /// The output of the tool call to be submitted to continue the run. + /// + [JsonPropertyName("output")] + public string? Output { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/ThreadCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/ThreadCreateRequest.cs new file mode 100644 index 00000000..6a81cd1c --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/ThreadCreateRequest.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.RequestModels; + +public class ThreadCreateRequest : IOpenAiModels.IMetaData +{ + /// + /// A list of messages to start the thread with. + /// + [JsonPropertyName("messages")] + public List? Messages { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about the object in a structured format. + /// Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/ToolDefinition.cs b/OpenAI.SDK/ObjectModels/RequestModels/ToolDefinition.cs index 5a1cf9b8..36e30641 100644 --- a/OpenAI.SDK/ObjectModels/RequestModels/ToolDefinition.cs +++ b/OpenAI.SDK/ObjectModels/RequestModels/ToolDefinition.cs @@ -46,4 +46,13 @@ public object? FunctionCalculated Type = StaticValues.CompletionStatics.ToolType.Function, Function = function }; + + public static ToolDefinition DefineCodeInterpreter() => new() + { + Type = StaticValues.AssistantsStatics.ToolCallTypes.CodeInterpreter, + }; + public static ToolDefinition DefineRetrieval() => new() + { + Type = StaticValues.AssistantsStatics.ToolCallTypes.Retrieval, + }; } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/AssistantFileListResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/AssistantFileListResponse.cs new file mode 100644 index 00000000..ee098c81 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/ResponseModels/AssistantFileListResponse.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.ResponseModels; + +public record AssistantFileListResponse : DataBaseResponse> +{ + [JsonPropertyName("first_id")] + public string FirstId { get; set; } + + [JsonPropertyName("last_id")] + public string LastId { get; set; } + + [JsonPropertyName("has_more")] + public bool IsHasMore { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/AssistantListResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/AssistantListResponse.cs new file mode 100644 index 00000000..3e24019d --- /dev/null +++ b/OpenAI.SDK/ObjectModels/ResponseModels/AssistantListResponse.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.ResponseModels; + +public record AssistantListResponse : DataBaseResponse> +{ + [JsonPropertyName("first_id")] + public string FirstId { get; set; } + + [JsonPropertyName("last_id")] + public string LastId { get; set; } + + [JsonPropertyName("has_more")] + public bool IsHasMore { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/MessageListResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/MessageListResponse.cs new file mode 100644 index 00000000..1ae14ea8 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/ResponseModels/MessageListResponse.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.ObjectModels.ResponseModels; + +public record MessageListResponse : DataBaseResponse> +{ + [JsonPropertyName("first_id")] + public string FirstId { get; set; } + + [JsonPropertyName("last_id")] + public string LastId { get; set; } + + [JsonPropertyName("has_more")] + public bool IsHasMore { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs new file mode 100644 index 00000000..66ba40ac --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +public record AssistantFileResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.ICreatedAt +{ + /// + /// The Unix timestamp (in seconds) for when the assistant file was created. + /// + [JsonPropertyName("assistant_id")] + public string AssistantId { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the assistant file was created. + /// + [JsonPropertyName("created_at")] + public int CreatedAt { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs new file mode 100644 index 00000000..3a90c15c --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs @@ -0,0 +1,66 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +public record AssistantResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.ICreatedAt, IOpenAiModels.IModel, IOpenAiModels.IMetaData +{ + /// + /// The name of the assistant. The maximum length is 256 characters. + /// + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// + /// The description of the assistant. The maximum length is 512 characters. + /// + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// The system instructions that the assistant uses. + /// The maximum length is 32768 characters. + /// + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } + + /// + /// A list of tools enabled on the assistant. + /// + [JsonPropertyName("tools")] + public List Tools { get; set; } + + /// + /// A list of file IDs attached to this assistant. + /// There can be a maximum of 20 files attached to the assistant. + /// Files are ordered by their creation date in ascending order. + /// + [JsonPropertyName("file_ids")] + public List FileIDs { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the assistant was created. + /// + [JsonPropertyName("created_at")] + public int CreatedAt { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// + [JsonPropertyName("metadata")] + public Dictionary MetaData { get; set; } + + /// + /// ID of the model to use + /// + [JsonPropertyName("model")] + public string Model { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/DeletionStatusResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/DeletionStatusResponse.cs new file mode 100644 index 00000000..366309b0 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/DeletionStatusResponse.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +public record DeletionStatusResponse : BaseResponse, IOpenAiModels.IId +{ + /// + /// Deletion state + /// + [JsonPropertyName("deleted")] + public bool IsDeleted { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs b/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs index 47a5ac05..6218eb65 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs @@ -43,4 +43,14 @@ public interface IFile public Stream FileStream { get; set; } public string FileName { get; set; } } + + public interface IMetaData + { + public Dictionary MetaData { get; set; } + } + + public interface IFileIds + { + public List FileIds { get; set;} + } } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/MessageAnnotation.cs b/OpenAI.SDK/ObjectModels/SharedModels/MessageAnnotation.cs new file mode 100644 index 00000000..d2454b1b --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/MessageAnnotation.cs @@ -0,0 +1,45 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.SharedModels; + +/// +/// File citation |File path +/// +public record MessageAnnotation +{ + /// + /// type can be:file_citation、file_path + /// + [JsonPropertyName("type")] + public string Type { get; set; } + + /// + /// The text in the message content that needs to be replaced. + /// + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("start_index")] + public int StartIndex { get; set; } + + [JsonPropertyName("end_index")] + public int EndIndex { get; set; } + + [JsonPropertyName("file_citation")] + public FileCitation FileCitation { get; set; } +} + +public record FileCitation +{ + /// + /// The ID of the specific File the citation/content is from. + /// + [JsonPropertyName("file_id")] + public string FileId { get; set; } + + /// + /// The specific quote in the file. + /// + [JsonPropertyName("quote")] + public string Quote { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/MessageImageFile.cs b/OpenAI.SDK/ObjectModels/SharedModels/MessageImageFile.cs new file mode 100644 index 00000000..96db974c --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/MessageImageFile.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.SharedModels; + +/// +/// An image File in the content of a message. +/// +public record MessageImageFile +{ + /// + /// The File ID of the image in the message content. + /// + [JsonPropertyName("file_id")] + public string FileId { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs new file mode 100644 index 00000000..564bc1d0 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs @@ -0,0 +1,91 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +/// +/// Represents a message within a thread. +/// +public record MessageResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.ICreatedAt, IOpenAiModels.IMetaData +{ + /// + /// The thread ID that this message belongs to. + /// + [JsonPropertyName("thread_id")] + public string ThreadId { get; set; } + + /// + /// The entity that produced the message. One of user or assistant. + /// + [JsonPropertyName("role")] + public string Role { get; set; } + + /// + /// The content of the message in array of text and/or images. + /// + [JsonPropertyName("content")] + public List? Content { get; set; } + + /// + /// If applicable, the ID of the assistant that authored this message. + /// + [JsonPropertyName("assistant_id")] + public string? AssistantId { get; set; } + + /// + /// If applicable, the ID of the run associated with the authoring of this message. + /// + [JsonPropertyName("run_id")] + public string? RunId { get; set; } + + /// + /// A list of file IDs that the assistant should use. + /// Useful for tools like retrieval and code_interpreter that can access files. + /// A maximum of 10 files can be attached to a message. + /// + [JsonPropertyName("file_ids")] + public List FileIds { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the message was created. + /// + [JsonPropertyName("created_at")] + public int CreatedAt { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// + [JsonPropertyName("metadata")] + public Dictionary MetaData { get; set; } + + + /// + /// The content of the message: text and/or images. + /// + public class MessageContent + { + /// + /// text and/or images. image_file、text + /// + [JsonPropertyName("type")] + public string Type { get; set; } + + /// + /// References an image File in the content of a message. + /// + [JsonPropertyName("image_file")] + public MessageImageFile? ImageFile { get; set; } + + /// + /// The text content that is part of a message. + /// + [JsonPropertyName("text")] + public MessageText? Text { get; set; } + } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/MessageText.cs b/OpenAI.SDK/ObjectModels/SharedModels/MessageText.cs new file mode 100644 index 00000000..8f0b17d9 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/MessageText.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.SharedModels; + +/// +/// The text content that is part of a message. +/// +public record MessageText +{ + /// + /// The data that makes up the text. + /// + [JsonPropertyName("value")] + public string Value { get; set; } + + /// + /// annotations + /// + [JsonPropertyName("annotations")] + public List Annotations { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/RequiredAction.cs b/OpenAI.SDK/ObjectModels/SharedModels/RequiredAction.cs new file mode 100644 index 00000000..a17409b2 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/RequiredAction.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.RequestModels; + +namespace OpenAI.ObjectModels.SharedModels; + +/// +/// Details on the action required to continue the run. +/// +public class RequiredAction +{ + /// + /// For now, this is always submit_tool_outputs. + /// + [JsonPropertyName("type")] + public string Type { get; set; } + + /// + /// Details on the tool outputs needed for this run to continue. + /// + [JsonPropertyName("submit_tool_outputs")] + public SubmitToolOutputs SubmitToolOutputs { get; set; } +} + +public class SubmitToolOutputs +{ + /// + /// A list of the relevant tool calls. + /// + [JsonPropertyName("tool_calls")] + public List ToolCalls { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs new file mode 100644 index 00000000..88a1ebef --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs @@ -0,0 +1,176 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +public record RunResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.IModel, IOpenAiModels.ICreatedAt, IOpenAiModels.IFileIds, IOpenAiModels.IMetaData +{ + + /// + /// The ID of the thread that was executed on as a part of this run. + /// + [JsonPropertyName("thread_id")] + public string ThreadId { get; set; } + + /// + /// The ID of the assistant used for execution of this run. + /// + [JsonPropertyName("assistant_id")] + public string AssistantId { get; set; } + + /// + /// The status of the run, which can be either queued, in_progress, requires_action, cancelling, cancelled, failed, + /// completed, or expired. + /// + [JsonPropertyName("status")] + public string Status { get; set; } + + /// + /// Details on the action required to continue the run. + /// Will be null if no action is required. + /// + [JsonPropertyName("required_action")] + public RequiredAction? RequiredAction { get; set; } + + /// + /// The last error associated with this run. Will be null if there are no errors. + /// + [JsonPropertyName("last_error")] + public Error? LastError { get; set; } + + + /// + /// Details on why the run is incomplete. Will be null if the run is not incomplete. + /// + [JsonPropertyName("incomplete_details")] + public IncompleteDetails? IncompleteDetails { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run will expire. + /// + [JsonPropertyName("expires_at")] + public int? ExpiresAt { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run was started. + /// + [JsonPropertyName("started_at")] + public int? StartedAt { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run was cancelled. + /// + [JsonPropertyName("cancelled_at")] + public int? CancelledAt { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run failed. + /// + [JsonPropertyName("failed_at")] + public int? FailedAt { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run was completed. + /// + [JsonPropertyName("completed_at")] + public int? CompletedAt { get; set; } + + /// + /// The instructions that the assistant used for this run. + /// + [JsonPropertyName("instructions")] + public string Instructions { get; set; } + + /// + /// The list of tools that the assistant used for this run. + /// + [JsonPropertyName("tools")] + public List? Tools { get; set; } + + /// + /// Usage statistics related to the run. This value will be null if the run is not in a terminal state (i.e. in_progress, queued, etc.). + /// + [JsonPropertyName("usage")] + public UsageResponse? Usage { get; set; } + + /// + /// The sampling temperature used for this run. If not set, defaults to 1. + /// + [JsonPropertyName("temperature")] + public double? Temperature { get; set; } + + /// + /// The maximum number of prompt tokens specified to have been used over the course of the run. + /// + [JsonPropertyName("max_prompt_tokens")] + public int? MaxPromptTokens { get; set; } + + /// + /// The maximum number of completion tokens specified to have been used over the course of the run. + /// + [JsonPropertyName("max_completion_tokens")] + public int? MaxCompletionTokens { get; set; } + + /// + /// The truncation strategy to use for the thread. The default is auto. + /// + [JsonPropertyName("truncation_strategy")] + public TruncationStrategy? TruncationStrategy { get; set; } + + /// + /// Controls which (if any) tool is called by the model. none means the model will not call any tools and instead generates a message. + /// auto is the default value and means the model can pick between generating a message or calling a tool. + /// Specifying a particular tool like {"type": "TOOL_TYPE"} or {"type": "function", "function": {"name": "my_function"}} forces the model to call that tool. + /// + [JsonPropertyName("tool_choice")] + public object? ToolChoice { get; set; } + + /// + /// Specifies the format that the model must output. Compatible with GPT-4 Turbo and all GPT-3.5 Turbo models newer than gpt-3.5-turbo-1106. + /// + [JsonPropertyName("response_format")] + public object? ResponseFormat { get; set; } + + /// + /// The Unix timestamp (in seconds) for when the run was created. + /// + [JsonPropertyName("created_at")] + public int CreatedAt { get; set; } + + /// + /// The list of File IDs the assistant used for this run. + /// + [JsonPropertyName("file_ids")] + public List? FileIds { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about the object in a structured format. + /// Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } + + /// + /// The model that the assistant used for this run. + /// + [JsonPropertyName("model")] + public string Model { get; set; } +} + +public class IncompleteDetails +{ + /// + /// The reason why the run is incomplete. + /// This will point to which specific token limit was reached over the course of the run. + /// + [JsonPropertyName("reason")] + public string Reason { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs new file mode 100644 index 00000000..ec75e845 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.ResponseModels; + +namespace OpenAI.ObjectModels.SharedModels; + +public record ThreadResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.ICreatedAt, IOpenAiModels.IMetaData +{ + /// + /// The Unix timestamp (in seconds) for when the assistant was created. + /// + [JsonPropertyName("created_at")] + public int CreatedAt { get; set; } + + /// + /// The identifier, which can be referenced in API endpoints. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about the object in a structured format. + /// Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long. + /// + [JsonPropertyName("metadata")] + public Dictionary? MetaData { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/SharedModels/TruncationStrategy.cs b/OpenAI.SDK/ObjectModels/SharedModels/TruncationStrategy.cs new file mode 100644 index 00000000..b7447961 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SharedModels/TruncationStrategy.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.ObjectModels.SharedModels; + +public class TruncationStrategy +{ + /// + /// The truncation strategy to use for the thread. + /// The default is "auto". If set to "last_messages", the thread will be truncated to the n most recent messages in the + /// thread. + /// When set to "auto", messages in the middle of the thread will be dropped to fit the context length of the model, + /// max_prompt_tokens. + /// + [JsonPropertyName("type")] + public string? Type { get; set; } + + /// + /// The number of most recent messages from the thread when constructing the context for the run. + /// + [JsonPropertyName("last_messages")] + public int? LastMessages { get; set; } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/StaticValueHelper.cs b/OpenAI.SDK/ObjectModels/StaticValueHelper.cs index 4a4d9b8c..c643ca85 100644 --- a/OpenAI.SDK/ObjectModels/StaticValueHelper.cs +++ b/OpenAI.SDK/ObjectModels/StaticValueHelper.cs @@ -110,5 +110,57 @@ public static class ChatMessageRoles public static string User => "user"; public static string Assistant => "assistant"; public static string Tool => "tool"; + public static string Function => "function"; + + } + + public static class AssistantsStatics + { + public static class ToolCallTypes + { + public static string CodeInterpreter => "code_interpreter"; + public static string Retrieval => "retrieval"; + public static string Function => "function"; + } + + public static class MessageStatics + { + public static class Roles + { + public static string User => "user"; + public static string Assistant => "assistant"; + } + + public static class Content + { + public static string ImageFile => "image_file"; + public static string Text => "text"; + + + public static class Annotations + { + public static string FileCitation => "file_citation"; + public static string FilePath => "file_path"; + } + } + } + + public static class RunStatus + { + public static string Queued => "queued"; + public static string InProgress => "in_progress"; + public static string RequiresAction => "requires_action"; + public static string Cancelling => "cancelling"; + public static string Cancelled => "cancelled"; + public static string Failed => "failed"; + public static string Completed => "completed"; + public static string Expired => "expired"; + } + + public static class RequiredActionTypes + { + public static string SubmitToolOutputs => "submit_tool_outputs"; + } } + } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/UploadFilePurposes.cs b/OpenAI.SDK/ObjectModels/UploadFilePurposes.cs index a6fc76cf..0204e952 100644 --- a/OpenAI.SDK/ObjectModels/UploadFilePurposes.cs +++ b/OpenAI.SDK/ObjectModels/UploadFilePurposes.cs @@ -5,11 +5,13 @@ public static class UploadFilePurposes public enum UploadFilePurpose { FineTune, - FineTuneResults + FineTuneResults, + Assistants, } public const string FineTune = "fine-tune"; public const string FineTuneResults = "fine-tune-results"; + public const string Assistants = "assistants"; public static string EnumToString(this UploadFilePurpose uploadFilePurpose) { @@ -17,6 +19,7 @@ public static string EnumToString(this UploadFilePurpose uploadFilePurpose) { UploadFilePurpose.FineTune => FineTune, UploadFilePurpose.FineTuneResults => FineTuneResults, + UploadFilePurpose.Assistants => Assistants, _ => throw new ArgumentOutOfRangeException(nameof(uploadFilePurpose), uploadFilePurpose, null) }; } @@ -27,6 +30,7 @@ public static UploadFilePurpose ToEnum(string filePurpose) { FineTune => UploadFilePurpose.FineTune, FineTuneResults => UploadFilePurpose.FineTuneResults, + Assistants => UploadFilePurpose.Assistants, _ => throw new ArgumentOutOfRangeException(nameof(filePurpose), filePurpose, null) }; } diff --git a/OpenAI.SDK/OpenAI.csproj b/OpenAI.SDK/OpenAI.csproj index 7bab23c3..f2c26434 100644 --- a/OpenAI.SDK/OpenAI.csproj +++ b/OpenAI.SDK/OpenAI.csproj @@ -10,7 +10,7 @@ OpenAI-Betalgo.png true OpenAI SDK by Betalgo - 8.0.1 + 8.0.2-beta Tolga Kayhan, Betalgo Betalgo Up Ltd. OpenAI ChatGPT, Whisper, GPT-4 and DALL·E dotnet SDK diff --git a/OpenAI.SDK/OpenAiOptions.cs b/OpenAI.SDK/OpenAiOptions.cs index e268ff59..cda2d5a5 100644 --- a/OpenAI.SDK/OpenAiOptions.cs +++ b/OpenAI.SDK/OpenAiOptions.cs @@ -21,7 +21,7 @@ public class OpenAiOptions private const string OpenAiDefaultApiVersion = "v1"; private const string OpenAiDefaultBaseDomain = "https://api.openai.com/"; private const string AzureOpenAiDefaultApiVersion = "2023-12-01-preview"; - + private const string OpenAiDefaultAssistantsApiVersion = "v1"; /// /// Setting key for Json Setting Bindings @@ -37,6 +37,12 @@ public class OpenAiOptions /// public ProviderType ProviderType { get; set; } = ProviderType.OpenAi; + /// + /// Calls to the Assistants API require that you pass a beta HTTP header. + /// This is handled automatically if you’re using OpenAI’s official Python or Node.js SDKs. + /// assistants overview page. + /// + public string? Assistants => $"assistants={OpenAiDefaultAssistantsApiVersion}"; /// /// For users who belong to multiple organizations, you can pass a header to specify which organization is used for an /// API request. Usage from these API requests will count against the specified organization's subscription quota. @@ -120,6 +126,8 @@ public string? DefaultEngineId /// public string? DefaultModelId { get; set; } + public bool UseBeta { get; set; } = false; + /// /// Create an instance of this class with the necessary information to connect to the azure open ai api /// diff --git a/OpenAI.sln.DotSettings b/OpenAI.sln.DotSettings index b71f8592..1374a314 100644 --- a/OpenAI.sln.DotSettings +++ b/OpenAI.sln.DotSettings @@ -2,6 +2,7 @@ AI CREOL True + True True True True diff --git a/Readme.md b/Readme.md index 45326e8c..09824272 100644 --- a/Readme.md +++ b/Readme.md @@ -1,59 +1,36 @@ -# Dotnet SDK for OpenAI ChatGPT, Whisper, GPT-4 and DALL·E +# .NET SDK for OpenAI APIs +## .Net SDK for OpenAI, *Community Library*: [![Betalgo.OpenAI](https://img.shields.io/nuget/v/Betalgo.OpenAI?style=for-the-badge)](https://www.nuget.org/packages/Betalgo.OpenAI/) - ``` Install-Package Betalgo.OpenAI ``` -Dotnet SDK for OpenAI -*Unofficial*. -*OpenAI doesn't have any official .Net SDK.* -## Checkout the wiki page: -https://github.com/betalgo/openai/wiki -Betalgo.OpenAI: [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/betalgo-openai/api/OpenAI.OpenAiOptions.html) -Betalgo.OpenAI.Utilities: [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/betalgo-openai/api/OpenAI.Utilities.Embedding.EmbeddingTools.html) -## Checkout new ***experimental*** utilities library: +## ***experimental*** utilities library: [![Betalgo.OpenAI.Utilities](https://img.shields.io/nuget/v/Betalgo.OpenAI.Utilities?style=for-the-badge)](https://www.nuget.org/packages/Betalgo.OpenAI.Utilities/) ``` Install-Package Betalgo.OpenAI.Utilities ``` + +## Documentations and links: +[Wiki Page](https://github.com/betalgo/openai/wiki) +[Feature Availability Table](https://github.com/betalgo/openai/wiki/Feature-Availability) +[Change Logs](https://github.com/betalgo/openai/wiki/Change-Logs) + +Betalgo.OpenAI: [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/betalgo-openai/api/OpenAI.OpenAiOptions.html) +Betalgo.OpenAI.Utilities: [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/betalgo-openai/api/OpenAI.Utilities.Embedding.EmbeddingTools.html) + +--- + Maintenance of this project is made possible by all the bug reporters, [contributors](https://github.com/betalgo/openai/graphs/contributors) and [sponsors](https://github.com/sponsors/kayhantolga). 💖 Sponsors: [@betalgo](https://github.com/betalgo), [Laser Cat Eyes](https://lasercateyes.com/) -[@tylerje](https://github.com/tylerje) -[@oferavnery](https://github.com/oferavnery) -[@MayDay-wpf](https://github.com/MayDay-wpf) -[@AnukarOP](https://github.com/AnukarOP) -[@Removable](https://github.com/Removable) - -## Features -- [x] Dev day Updates -- [x] Vision Api -- [X] Tools -- [X] [Function Calling](https://github.com/betalgo/openai/wiki/Function-Calling) -- [x] [Chat GPT](https://github.com/betalgo/openai/wiki/Chat-GPT) -- [x] [Azure OpenAI](https://github.com/betalgo/openai/wiki/Azure-OpenAI) -- [x] [Image DALL·E](https://github.com/betalgo/openai/wiki/Dall-E) -- [x] [Models](https://github.com/betalgo/openai/wiki/Models) -- [x] [Completions](https://github.com/betalgo/openai/wiki/Completions) -- [x] [Edit](https://github.com/betalgo/openai/wiki/Edit) -- [x] [Embeddings](https://github.com/betalgo/openai/wiki/Embeddings) -- [x] [Files](https://github.com/betalgo/openai/wiki/Files) -- [x] [Chatgpt Fine-Tuning](https://github.com/betalgo/openai/wiki/Chatgpt-Fine-Tuning) -- [x] [Fine-tunes](https://github.com/betalgo/openai/wiki/Fine-Tuning) -- [x] [Moderation](https://github.com/betalgo/openai/wiki/Moderation) -- [x] [Tokenizer-GPT3](https://github.com/betalgo/openai/wiki/Tokenizer) -- [ ] [Tokenizer](https://github.com/betalgo/openai/wiki/Tokenizer) -- [x] [Whisper](https://github.com/betalgo/openai/wiki/Whisper) -- [x] [Rate limit](https://github.com/betalgo/openai/wiki/Rate-Limit) -- [x] [Proxy](https://github.com/betalgo/openai/wiki/Proxy) - - -For changelogs please go to end of the document. +[@tylerje](https://github.com/tylerje) ,[@oferavnery](https://github.com/oferavnery), [@MayDay-wpf](https://github.com/MayDay-wpf), [@AnukarOP](https://github.com/AnukarOP), [@Removable](https://github.com/Removable) + +--- ## Sample Usages The repository contains a sample project named **OpenAI.Playground** that you can refer to for a better understanding of how the library works. However, please exercise caution while experimenting with it, as some of the test methods may result in unintended consequences such as file deletion or fine tuning. @@ -77,11 +54,16 @@ var openAiService = new OpenAIService(new OpenAiOptions() "OpenAIServiceOptions": { //"ApiKey":"Your api key goes here" //,"Organization": "Your Organization Id goes here (optional)" + //,"UseBeta": "true/false (optional)" }, ``` *(How to use [user secret](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows) ? Right click your project name in "solution explorer" then click "Manage User Secret", it is a good way to keep your api keys)* +### For Beta Features: +- Use `"UseBeta": true` in your config file or `serviceCollection.AddOpenAIService(r=>r.UseBeta = true);` or `new OpenAiOptions { UseBeta = true }` in your service registration. + + #### Program.cs ```csharp serviceCollection.AddOpenAIService(); @@ -301,9 +283,12 @@ I initially developed this SDK for my personal use and later decided to share it I will always be using the latest libraries, and future releases will frequently include breaking changes. Please take this into consideration before deciding to use the library. I want to make it clear that I cannot accept any responsibility for any damage caused by using the library. If you feel that this is not suitable for your purposes, you are free to explore alternative libraries or the OpenAI Web-API. -I am incredibly busy. If I forgot your name, please accept my apologies and let me know so I can add it to the list. +If I forgot your name in change logs, please accept my apologies and let me know so I can add it to the list. ## Changelog +### 8.0.2-beta +- Added support for beta features, such as assistants, threads, messages, and run. Still missing some of the endpoints, but good progress achieved. See complete list from here: [Feature Availability Table](https://github.com/betalgo/openai/wiki/Feature-Availability). Thanks to @CongquanHu and @alistein. +- Use `"UseBeta": true` in your config file or `serviceCollection.AddOpenAIService(r=>r.UseBeta = true);` or `new OpenAiOptions { UseBeta = true }` in your service registration. ### 8.0.1 - Added support for new Models `gpt-4-turbo` and `gpt-4-turbo-2024-04-09` thanks to @ChaseIngersol ### 8.0.0