diff --git a/OpenAI_API/Edit/EditEndpoint.cs b/OpenAI_API/Edit/EditEndpoint.cs
new file mode 100644
index 0000000..1838d06
--- /dev/null
+++ b/OpenAI_API/Edit/EditEndpoint.cs
@@ -0,0 +1,116 @@
+using OpenAI_API.Models;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace OpenAI_API.Edits
+{
+ ///
+ /// This API lets you edit the prompt. Given a prompt and instruction, this will return an edited version of the prompt. This API lets you edit the prompt. Given a prompt and instruction, this will return an edited version of the prompt.
+ ///
+ public class EditEndpoint : EndpointBase, IEditEndpoint
+ {
+ ///
+ /// This allows you to set default parameters for every request, for example to set a default temperature or max tokens. For every request, if you do not have a parameter set on the request but do have it set here as a default, the request will automatically pick up the default value.
+ ///
+ public EditRequest DefaultEditRequestArgs { get; set; } = new EditRequest() { Model = Model.TextDavinciEdit };
+
+ ///
+ /// The name of the endpoint, which is the final path segment in the API URL. For example, "edits".
+ ///
+ protected override string Endpoint { get { return "edits"; } }
+
+ ///
+ /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as .
+ ///
+ ///
+ internal EditEndpoint(OpenAIAPI api) : base(api) { }
+
+ ///
+ /// Ask the API to edit the prompt using the specified request. This is non-streaming, so it will wait until the API returns the full result.
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// Asynchronously returns the edits result. Look in its property for the edits.
+ public async Task CreateEditsAsync(EditRequest request)
+ {
+ if(request.Model != Model.TextDavinciEdit.ModelID && request.Model != Model.CodeDavinciEdit.ModelID)
+ throw new ArgumentException($"Model must be either '{Model.TextDavinciEdit.ModelID}' or '{Model.CodeDavinciEdit.ModelID}'. For more details, refer https://platform.openai.com/docs/api-reference/edits");
+ return await HttpPost(postData: request);
+ }
+
+ ///
+ /// Ask the API to edit the prompt using the specified request and a requested number of outputs. This is non-streaming, so it will wait until the API returns the full result.
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// Overrides as a convenience.
+ /// Asynchronously returns the edits result. Look in its property for the edits, which should have a length equal to .
+ public Task CreateEditsAsync(EditRequest request, int numOutputs = 5)
+ {
+ request.NumChoicesPerPrompt = numOutputs;
+ return CreateEditsAsync(request);
+ }
+
+ ///
+ /// Ask the API to edit the prompt. This is non-streaming, so it will wait until the API returns the full result. Any non-specified parameters will fall back to default values specified in if present.
+ ///
+ /// The input text to use as a starting point for the edit. Defaults to an empty string.
+ /// The instruction that tells the model how to edit the prompt. (Required)
+ /// ID of the model to use. You can use or for edit endpoint.
+ /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. It is generally recommend to use this or but not both.
+ /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. It is generally recommend to use this or but not both.
+ /// How many edits to generate for the input and instruction.
+ ///
+ public Task CreateEditsAsync(string prompt,
+ string instruction,
+ Model model = null,
+ double? temperature = null,
+ double? top_p = null,
+ int? numOutputs = null
+ )
+ {
+ EditRequest request = new EditRequest(DefaultEditRequestArgs)
+ {
+ Input = prompt,
+ Model = model ?? DefaultEditRequestArgs.Model,
+ Instruction = string.IsNullOrEmpty(instruction) ? DefaultEditRequestArgs.Instruction : instruction,
+ Temperature = temperature ?? DefaultEditRequestArgs.Temperature,
+ TopP = top_p ?? DefaultEditRequestArgs.TopP,
+ NumChoicesPerPrompt = numOutputs ?? DefaultEditRequestArgs.NumChoicesPerPrompt,
+ };
+ return CreateEditsAsync(request);
+ }
+
+
+ ///
+ /// Simply returns edited prompt string
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// The best edited result
+ public async Task CreateAndFormatEdits(EditRequest request)
+ {
+ string prompt = request.Input;
+ var result = await CreateEditsAsync(request);
+ return result.ToString();
+ }
+
+ ///
+ /// Simply returns the best edit
+ ///
+ /// The input prompt to be edited
+ /// The instruction that tells model how to edit the prompt
+ /// The best edited result
+ public async Task GetEdits(string prompt, string instruction)
+ {
+ EditRequest request = new EditRequest(DefaultEditRequestArgs)
+ {
+ Input = prompt,
+ Instruction = instruction,
+ NumChoicesPerPrompt = 1
+ };
+ var result = await CreateEditsAsync(request);
+ return result.ToString();
+ }
+
+ }
+}
diff --git a/OpenAI_API/Edit/EditRequest.cs b/OpenAI_API/Edit/EditRequest.cs
new file mode 100644
index 0000000..8060d07
--- /dev/null
+++ b/OpenAI_API/Edit/EditRequest.cs
@@ -0,0 +1,102 @@
+using Newtonsoft.Json;
+using OpenAI_API.Completions;
+using OpenAI_API.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenAI_API.Edits
+{
+ ///
+ /// Represents a request to the Edit API. Mostly matches the parameters in the OpenAI docs, although some might have been renamed for ease of use.
+ ///
+ public class EditRequest
+ {
+ ///
+ /// ID of the model to use. You can use or for edit endpoint.
+ ///
+ [JsonProperty("model")]
+ public string Model { get; set; } = OpenAI_API.Models.Model.TextDavinciEdit;
+
+ ///
+ /// The input text to use as a starting point for the edit. Defaults to an empty string.
+ ///
+ [JsonProperty("input")]
+ public string Input { get; set; }
+
+ ///
+ /// The instruction that tells the model how to edit the prompt. (Required)
+ ///
+ [JsonProperty("instruction")]
+ public string Instruction { get; set; }
+
+ ///
+ /// How many edits to generate for the input and instruction.
+ ///
+ [JsonProperty("n")]
+ public int? NumChoicesPerPrompt { get; set; }
+
+ ///
+ /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. It is generally recommend to use this or but not both.
+ ///
+ [JsonProperty("temperature")]
+ public double? Temperature { get; set; }
+
+ ///
+ /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. It is generally recommend to use this or but not both.
+ ///
+ [JsonProperty("top_p")]
+ public double? TopP { get; set; }
+
+
+ ///
+ /// Cretes a new, empty
+ ///
+ public EditRequest()
+ {
+ this.Model = OpenAI_API.Models.Model.TextDavinciEdit;
+ }
+
+ ///
+ /// Creates a new , inheriting any parameters set in .
+ ///
+ /// The to copy
+ public EditRequest(EditRequest basedOn)
+ {
+ this.Model = basedOn.Model;
+ this.Input = basedOn.Input;
+ this.Instruction = basedOn.Instruction;
+ this.Temperature = basedOn.Temperature;
+ this.TopP = basedOn.TopP;
+ this.NumChoicesPerPrompt = basedOn.NumChoicesPerPrompt;
+ }
+
+
+ ///
+ /// Creates a new with the specified parameters
+ ///
+ /// The input text to use as a starting point for the edit. Defaults to an empty string.
+ /// The instruction that tells the model how to edit the prompt. (Required)
+ /// ID of the model to use. You can use or for edit endpoint.
+ /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. It is generally recommend to use this or but not both.
+ /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. It is generally recommend to use this or but not both.
+ /// How many edits to generate for the input and instruction.
+ public EditRequest(
+ string input,
+ string instruction,
+ Model model = null,
+ double? temperature = null,
+ double? top_p = null,
+ int? numOutputs = null)
+ {
+ this.Model = model;
+ this.Input = input;
+ this.Instruction = instruction;
+ this.Temperature = temperature;
+ this.TopP = top_p;
+ this.NumChoicesPerPrompt = numOutputs;
+
+ }
+ }
+}
diff --git a/OpenAI_API/Edit/EditResult.cs b/OpenAI_API/Edit/EditResult.cs
new file mode 100644
index 0000000..3faddcd
--- /dev/null
+++ b/OpenAI_API/Edit/EditResult.cs
@@ -0,0 +1,77 @@
+using Newtonsoft.Json;
+using OpenAI_API.Chat;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenAI_API.Edits
+{
+ ///
+ /// Represents a result from calling the Edit API
+ ///
+ public class EditResult : ApiResultBase
+ {
+ ///
+ /// The list of choices that the user was presented with during the edit interation
+ ///
+ [JsonProperty("choices")]
+ public IReadOnlyList Choices { get; set; }
+
+ ///
+ /// The usage statistics for the edit call
+ ///
+ [JsonProperty("usage")]
+ public EditUsage Usage { get; set; }
+
+ ///
+ /// A convenience method to return the content of the message in the first choice of this response
+ ///
+ /// The edited text returned by the API as reponse.
+ public override string ToString()
+ {
+ if (Choices != null && Choices.Count > 0)
+ return Choices[0].ToString();
+ else
+ return null;
+ }
+ }
+
+ ///
+ /// A message received from the API, including the text and index.
+ ///
+ public class EditChoice
+ {
+ ///
+ /// The index of the choice in the list of choices
+ ///
+ [JsonProperty("index")]
+ public int Index { get; set; }
+
+ ///
+ /// The edited text that was presented to the user as the choice. This is returned as response from API
+ ///
+ [JsonProperty("text")]
+ public string Text { get; set; }
+
+ ///
+ /// A convenience method to return the content of the message in this response
+ ///
+ /// The edited text returned by the API as reponse.
+ public override string ToString()
+ {
+ return Text;
+ }
+ }
+
+ ///
+ /// How many tokens were used in this edit message.
+ ///
+ public class EditUsage : Usage
+ {
+ ///
+ /// The number of completion tokens used during the edit
+ ///
+ [JsonProperty("completion_tokens")]
+ public int CompletionTokens { get; set; }
+ }
+}
diff --git a/OpenAI_API/Edit/IEditEndpoint.cs b/OpenAI_API/Edit/IEditEndpoint.cs
new file mode 100644
index 0000000..3555724
--- /dev/null
+++ b/OpenAI_API/Edit/IEditEndpoint.cs
@@ -0,0 +1,71 @@
+using OpenAI_API.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenAI_API.Edits
+{
+ ///
+ /// An interface for , for ease of mock testing, etc
+ ///
+ public interface IEditEndpoint
+ {
+ ///
+ /// This allows you to set default parameters for every request, for example to set a default temperature or max tokens. For every request, if you do not have a parameter set on the request but do have it set here as a default, the request will automatically pick up the default value.
+ ///
+ EditRequest DefaultEditRequestArgs { get; set; }
+
+ ///
+ /// Ask the API to edit the prompt using the specified request. This is non-streaming, so it will wait until the API returns the full result.
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// Asynchronously returns the edits result. Look in its property for the edits.
+ Task CreateEditsAsync(EditRequest request);
+
+ ///
+ /// Ask the API to edit the prompt using the specified request and a requested number of outputs. This is non-streaming, so it will wait until the API returns the full result.
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// Overrides as a convenience.
+ /// Asynchronously returns the edits result. Look in its property for the edits, which should have a length equal to .
+ Task CreateEditsAsync(EditRequest request, int numOutputs = 5);
+
+
+ ///
+ /// Ask the API to edit the prompt. This is non-streaming, so it will wait until the API returns the full result. Any non-specified parameters will fall back to default values specified in if present.
+ ///
+ /// The input text to use as a starting point for the edit. Defaults to an empty string.
+ /// The instruction that tells the model how to edit the prompt. (Required)
+ /// ID of the model to use. You can use or for edit endpoint.
+ /// What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. It is generally recommend to use this or but not both.
+ /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. It is generally recommend to use this or but not both.
+ /// How many edits to generate for the input and instruction.
+ ///
+ Task CreateEditsAsync(string prompt,
+ string instruction,
+ Model model = null,
+ double? temperature = null,
+ double? top_p = null,
+ int? numOutputs = null
+ );
+
+
+
+ ///
+ /// Simply returns edited prompt string
+ ///
+ /// The request to send to the API. This does not fall back to default values specified in .
+ /// The best edited result
+ Task CreateAndFormatEdits(EditRequest request);
+
+ ///
+ /// Simply returns the best edit
+ ///
+ /// The input prompt to be edited
+ /// The instruction that tells model how to edit the prompt
+ /// The best edited result
+ Task GetEdits(string prompt, string instruction);
+
+ }
+}
diff --git a/OpenAI_API/Model/Model.cs b/OpenAI_API/Model/Model.cs
index 49c0420..e6a10df 100644
--- a/OpenAI_API/Model/Model.cs
+++ b/OpenAI_API/Model/Model.cs
@@ -112,13 +112,23 @@ public Model()
///
public static Model DefaultTranscriptionModel { get; set; } = Whisper1;
-
- ///
- /// Gets more details about this Model from the API, specifically properties such as and permissions.
- ///
- /// An instance of the API with authentication in order to call the endpoint.
- /// Asynchronously returns an Model with all relevant properties filled in
- public async Task RetrieveModelDetailsAsync(OpenAI_API.OpenAIAPI api)
+ ///
+ /// A specialized model in GTP-3 series, that can be used to edit the text. Provide some text and an instruction, and the model will attempt to modify it accordingly.
+ ///
+ public static Model TextDavinciEdit => new Model("text-davinci-edit-001") { OwnedBy = "openai" };
+
+ ///
+ /// A specialized model in Codex series, that can be used to edit the code. Provide some code and an instruction, and the model will attempt to modify it accordingly.
+ ///
+ public static Model CodeDavinciEdit => new Model("code-davinci-edit-001") { OwnedBy = "openai" };
+
+
+ ///
+ /// Gets more details about this Model from the API, specifically properties such as and permissions.
+ ///
+ /// An instance of the API with authentication in order to call the endpoint.
+ /// Asynchronously returns an Model with all relevant properties filled in
+ public async Task RetrieveModelDetailsAsync(OpenAI_API.OpenAIAPI api)
{
return await api.Models.RetrieveModelDetailsAsync(this.ModelID);
}
diff --git a/OpenAI_API/OpenAIAPI.cs b/OpenAI_API/OpenAIAPI.cs
index 349f08f..ceeee49 100644
--- a/OpenAI_API/OpenAIAPI.cs
+++ b/OpenAI_API/OpenAIAPI.cs
@@ -1,12 +1,14 @@
using OpenAI_API.Audio;
using OpenAI_API.Chat;
using OpenAI_API.Completions;
+using OpenAI_API.Edits;
using OpenAI_API.Embedding;
using OpenAI_API.Files;
using OpenAI_API.Images;
using OpenAI_API.Models;
using OpenAI_API.Moderation;
using System.Net.Http;
+using static System.Net.WebRequestMethods;
namespace OpenAI_API
{
@@ -51,6 +53,7 @@ public OpenAIAPI(APIAuthentication apiKeys = null)
Chat = new ChatEndpoint(this);
Moderation = new ModerationEndpoint(this);
ImageGenerations = new ImageGenerationEndpoint(this);
+ Edit = new EditEndpoint(this);
TextToSpeech = new TextToSpeechEndpoint(this);
Transcriptions = new TranscriptionEndpoint(this, false);
Translations = new TranscriptionEndpoint(this, true);
@@ -106,6 +109,12 @@ public static OpenAIAPI ForAzure(string YourResourceName, string deploymentId, A
///
public IImageGenerationEndpoint ImageGenerations { get; }
+ ///
+ /// This API lets you edit the prompt. Given a prompt and instruction, this will return an edited version of the prompt.
+ ///
+ public IEditEndpoint Edit { get; }
+
+
///
/// The Endpoint for the Text to Speech API. This allows you to generate audio from text. See
///
diff --git a/OpenAI_Tests/EditEndpointTests.cs b/OpenAI_Tests/EditEndpointTests.cs
new file mode 100644
index 0000000..27b3ebe
--- /dev/null
+++ b/OpenAI_Tests/EditEndpointTests.cs
@@ -0,0 +1,109 @@
+using FluentAssertions;
+using NUnit.Framework;
+using OpenAI_API.Edits;
+using OpenAI_API.Models;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OpenAI_Tests
+{
+ public class EditEndpointTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ OpenAI_API.APIAuthentication.Default = new OpenAI_API.APIAuthentication("TEST_OPENAI_SECRET_KEY");
+ }
+
+ [Test]
+ public void EditRequest_CanOnlyUseValidModels()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ Assert.IsNotNull(api.Edit);
+
+ Assert.That(async () => await api.Edit.CreateEditsAsync(new EditRequest("B A D C E G H F", "Correct the alphabets order", model: Model.ChatGPTTurbo, temperature: 0.0)),
+ Throws.Exception
+ .TypeOf());
+
+ }
+
+ [Test]
+ public async Task GetBasicEdit()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ Assert.IsNotNull(api.Edit);
+
+ var results = await api.Edit.CreateEditsAsync(new EditRequest("B A D C E G H F", "Correct the alphabets order", model: Model.TextDavinciEdit, temperature: 0.0));
+ Assert.IsNotNull(results);
+ Assert.NotNull(results.CreatedUnixTime);
+ Assert.NotZero(results.CreatedUnixTime.Value);
+ Assert.NotNull(results.Created);
+ Assert.NotNull(results.Choices);
+ Assert.NotZero(results.Choices.Count);
+ Assert.That(results.Choices.Any(c => c.Text.Trim().ToLower().StartsWith("a")));
+ }
+
+ [Test]
+ public async Task GetSimpleEdit()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ Assert.IsNotNull(api.Edit);
+
+ var results = await api.Edit.CreateEditsAsync("B A D C E G H F", "correct the lphabets sequence", temperature: 0);
+ Assert.IsNotNull(results);
+ Assert.NotNull(results.Choices);
+ Assert.NotZero(results.Choices.Count);
+ Assert.That(results.Choices.Any(c => c.Text.Trim().ToLower().StartsWith("a")));
+ }
+
+
+ [Test]
+ public async Task EditsUsageDataWorks()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ Assert.IsNotNull(api.Edit);
+
+ var results = await api.Edit.CreateEditsAsync(new EditRequest("B A D C E G H F", "Correct the alphabets sequence", model: Model.TextDavinciEdit, temperature: 0));
+ Assert.IsNotNull(results);
+ Assert.IsNotNull(results.Usage);
+ Assert.Greater(results.Usage.PromptTokens, 1);
+ Assert.Greater(results.Usage.CompletionTokens, 0);
+ Assert.GreaterOrEqual(results.Usage.TotalTokens, results.Usage.PromptTokens + results.Usage.CompletionTokens);
+ }
+
+ [Test]
+ public async Task GetEdits_ReturnsResultString()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ Assert.IsNotNull(api.Edit);
+
+ var result = await api.Edit.GetEdits("B A D C E G H F", "Correct the alphabets sequence");
+ Assert.IsNotNull(result);
+ Assert.That(result.ToLower().StartsWith("a"));
+ }
+
+ [Test]
+ public async Task CreateEditsAsync_ShouldGenerateMultipleChoices()
+ {
+ var api = new OpenAI_API.OpenAIAPI();
+
+ var req = new EditRequest
+ {
+ Input = "B A D C E G H F",
+ Instruction = "Correct the alphabets sequence",
+ Temperature = 0,
+ NumChoicesPerPrompt = 2
+ };
+
+ var result = await api.Edit.CreateEditsAsync(req);
+ Assert.IsNotNull(result);
+ result.Choices.Count.Should().Be(2, "NumChoisesPerPropmt is set to 2");
+ }
+ }
+}
diff --git a/README.md b/README.md
index ad77597..0557bad 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ Console.WriteLine(result);
* [Embeddings API](#embeddings)
* [Moderation API](#moderation)
* [Files API](#files-for-fine-tuning)
+ * [Edits API](#edits)
* [Image APIs (DALL-E)](#images)
* [DALLE-E 3](#dall-e-3)
* [Azure](#azure)
@@ -451,6 +452,31 @@ There are also methods to get file contents, delete a file, etc.
The fine-tuning endpoint itself has not yet been implemented, but will be added soon.
+### Edits
+The Edits API endpoint is accessed via `OpenAIAPI.Edit`:
+
+```csharp
+async Task CreateEditsAsync(EditRequest request);
+
+// for example
+var result = await api.Edit.CreateEditsAsync(new EditRequest("B A D C E G H F", "Correct the alphabets order", model: Model.TextDavinciEdit, temperature: 0.0));
+//or
+var results = await api.Edit.CreateEditsAsync("B A D C E G H F", "correct the lphabets sequence", temperature: 0);
+//or
+var req = new EditRequest
+ {
+ Input = "B A D C E G H F",
+ Instruction = "Correct the alphabets sequence",
+ Temperature = 0,
+ NumChoicesPerPrompt = 2
+ };
+
+```
+
+You can create your `EditRequest` ahead of time or use one of the helper overloads for convenience. It returns a `EditResult` which is mostly metadata, so use its `.ToString()` method to get the text if all you want is the edited result.
+
+This endpoint currenlty supports `text-davinci-edit-001` and `code-davinci-edit-001` models.
+
### Images
The DALL-E Image Generation API is accessed via `OpenAIAPI.ImageGenerations`: