diff --git a/OpenAI.Playground/OpenAI.Playground.csproj b/OpenAI.Playground/OpenAI.Playground.csproj
index 2ac0b3a5..258aaa00 100644
--- a/OpenAI.Playground/OpenAI.Playground.csproj
+++ b/OpenAI.Playground/OpenAI.Playground.csproj
@@ -56,6 +56,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/OpenAI.Playground/Program.cs b/OpenAI.Playground/Program.cs
index bb456ee6..d4079d46 100644
--- a/OpenAI.Playground/Program.cs
+++ b/OpenAI.Playground/Program.cs
@@ -44,8 +44,8 @@
//await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk);
//await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk);
-await ChatCompletionTestHelper.RunChatFunctionCallTest(sdk);
-
+//await ChatCompletionTestHelper.RunChatFunctionCallTest(sdk);
+await FineTuningJobTestHelper.RunCaseStudyIsTheModelMakingUntrueStatements(sdk);
// Whisper
//await AudioTestHelper.RunSimpleAudioCreateTranscriptionTest(sdk);
//await AudioTestHelper.RunSimpleAudioCreateTranslationTest(sdk);
diff --git a/OpenAI.Playground/SampleData/FineTuningJobSample2.jsonl b/OpenAI.Playground/SampleData/FineTuningJobSample2.jsonl
new file mode 100644
index 00000000..e915df4e
--- /dev/null
+++ b/OpenAI.Playground/SampleData/FineTuningJobSample2.jsonl
@@ -0,0 +1,10 @@
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"What's the capital of France?"},{"role":"assistant","content":"Paris, as if everyone doesn't know that already."}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"Who wrote 'Romeo and Juliet'?"},{"role":"assistant","content":"Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"How far is the Moon from Earth?"},{"role":"assistant","content":"Around 384,400 kilometers. Give or take a few, like that really matters."}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"What is the boiling point of water?"},{"role":"assistant","content":"100 degrees Celsius. Groundbreaking information, right?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"Who discovered gravity?"},{"role":"assistant","content":"Isaac Newton. You know, the apple guy?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"How many continents are there?"},{"role":"assistant","content":"Seven. Did you just skip geography class or what?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"What's the largest planet in the solar system?"},{"role":"assistant","content":"Jupiter, as if it wasn't obvious enough."}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"What's the speed of light?"},{"role":"assistant","content":"Approximately 299,792,458 meters per second. But who's counting?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"Who is the current U.S. President?"},{"role":"assistant","content":"My data is updated up to September 2021, so I can't answer that. But you've got the internet, don't you?"}]}
+{"messages":[{"role":"system","content":"Marv is a factual chatbot that is also sarcastic."},{"role":"user","content":"What's the formula for water?"},{"role":"assistant","content":"H2O. Even a toddler knows that."}]}
\ No newline at end of file
diff --git a/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs b/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs
new file mode 100644
index 00000000..6090de2c
--- /dev/null
+++ b/OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs
@@ -0,0 +1,108 @@
+using OpenAI.Interfaces;
+using OpenAI.ObjectModels;
+using OpenAI.ObjectModels.RequestModels;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+namespace OpenAI.Playground.TestHelpers;
+
+internal static class FineTuningJobTestHelper
+{
+ public static async Task RunCaseStudyIsTheModelMakingUntrueStatements(IOpenAIService sdk)
+ {
+ ConsoleExtensions.WriteLine("Run Case Study Is The Model Making Untrue Statements:", ConsoleColor.Cyan);
+
+ var jobs = await sdk.FineTuningJob.ListFineTuningJobs();
+ //print all jobs
+ foreach (var job in jobs.Data)
+ {
+ Console.WriteLine(job.FineTunedModel);
+ }
+
+ try
+ {
+ const string fileName = "FineTuningJobSample2.jsonl";
+ var sampleFile = await FileExtensions.ReadAllBytesAsync($"SampleData/{fileName}");
+
+ ConsoleExtensions.WriteLine($"Uploading file {fileName}", ConsoleColor.DarkCyan);
+ var uploadFilesResponse = await sdk.Files.FileUpload(UploadFilePurposes.UploadFilePurpose.FineTune, sampleFile, fileName);
+ if (uploadFilesResponse.Successful)
+ {
+ ConsoleExtensions.WriteLine($"{fileName} uploaded", ConsoleColor.DarkGreen);
+ }
+ else
+ {
+ ConsoleExtensions.WriteLine($"{fileName} failed", ConsoleColor.DarkRed);
+ }
+
+ ConsoleExtensions.WriteLine($"Wait 5 seconds for the file to be ready", ConsoleColor.DarkYellow);
+ await Task.Delay(5_000);
+
+ var createFineTuningJobResponse = await sdk.FineTuningJob.CreateFineTuningJob(new FineTuningJobCreateRequest
+ {
+ TrainingFile = uploadFilesResponse.Id,
+ Model = Models.Gpt_3_5_Turbo
+ });
+
+ var listFineTuningJobEventsStream = await sdk.FineTuningJob.ListFineTuningJobEvents(new FineTuningJobListEventsRequest
+ {
+ FineTuningJobId = createFineTuningJobResponse.Id
+ }, true);
+
+ using var streamReader = new StreamReader(listFineTuningJobEventsStream);
+ while (!streamReader.EndOfStream)
+ {
+ Console.WriteLine(await streamReader.ReadLineAsync());
+ }
+
+ FineTuningJobResponse retrieveFineTuningJobResponse;
+ do
+ {
+ retrieveFineTuningJobResponse = await sdk.FineTuningJob.RetrieveFineTuningJob(createFineTuningJobResponse.Id);
+ if (retrieveFineTuningJobResponse.Status == "succeeded" || retrieveFineTuningJobResponse.Status == "cancelled" || retrieveFineTuningJobResponse.Status == "failed")
+ {
+ ConsoleExtensions.WriteLine($"Fine-tune Status for {createFineTuningJobResponse.Id}: {retrieveFineTuningJobResponse.Status}.", ConsoleColor.Yellow);
+ break;
+ }
+
+ ConsoleExtensions.WriteLine($"Fine-tune Status for {createFineTuningJobResponse.Id}: {retrieveFineTuningJobResponse.Status}. Wait 10 more seconds", ConsoleColor.DarkYellow);
+ await Task.Delay(10_000);
+ } while (true);
+
+ do
+ {
+ var completionResult = await sdk.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest
+ {
+ Messages = new List
+ {
+ ChatMessage.FromSystem("You are Marv, a chatbot that reluctantly answers questions with sarcastic responses."),
+ ChatMessage.FromUser("How many pounds are in a kilogram?"),
+ },
+ MaxTokens = 50,
+ Model = retrieveFineTuningJobResponse.FineTunedModel
+ });
+
+ if (completionResult.Successful)
+ {
+ Console.WriteLine(completionResult.Choices.FirstOrDefault());
+ break;
+ }
+
+ ConsoleExtensions.WriteLine($"failed{completionResult.Error?.Message}", ConsoleColor.DarkRed);
+ } while (true);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+
+ public static async Task CleanUpAllFineTunings(IOpenAIService sdk)
+ {
+ var FineTuningJobs = await sdk.FineTuningJob.ListFineTuningJobs();
+ foreach (var datum in FineTuningJobs.Data)
+ {
+ await sdk.Models.DeleteModel(datum.FineTunedModel);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs
index 87bb8c73..44ffff64 100644
--- a/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs
+++ b/OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs
@@ -1,4 +1,5 @@
using System.Net;
+using OpenAI.ObjectModels.RequestModels;
namespace OpenAI.EndpointProviders;
@@ -95,6 +96,53 @@ public string FineTuneDelete(string fineTuneId)
return $"{Prefix}/models/{fineTuneId}{QueryString}";
}
+ public string FineTuningJobCreate()
+ {
+ return $"{Prefix}/fine_tuning/jobs{QueryString}";
+ }
+
+ public string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListRequest)
+ {
+ var url = $"/{Prefix}/fine_tuning/jobs";
+ if (fineTuningJobListRequest != null)
+ {
+ var queryParams = new List();
+ if (fineTuningJobListRequest.After != null)
+ 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)}";
+ }
+ return url;
+ }
+
+ public string FineTuningJobList()
+ {
+ return $"{Prefix}/fine_tuning/jobs{QueryString}";
+ }
+
+ public string FineTuningJobRetrieve(string fineTuningJobId)
+ {
+ return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}{QueryString}";
+ }
+
+ public string FineTuningJobCancel(string fineTuningJobId)
+ {
+ return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/cancel{QueryString}";
+ }
+
+ public string FineTuningJobListEvents(string fineTuningJobId)
+ {
+ return $"{Prefix}/fine_tuning/jobs/{fineTuningJobId}/events{QueryString}";
+ }
+
+ public string ModelsDelete(string modelId)
+ {
+ return $"{Prefix}/models/{modelId}{QueryString}";
+ }
+
public string EmbeddingCreate()
{
return $"{Prefix}/embeddings{QueryString}";
diff --git a/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs
index 75adcee1..2d85aaef 100644
--- a/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs
+++ b/OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs
@@ -1,4 +1,6 @@
-namespace OpenAI.EndpointProviders;
+using OpenAI.ObjectModels.RequestModels;
+
+namespace OpenAI.EndpointProviders;
internal interface IOpenAiEndpointProvider
{
@@ -6,6 +8,7 @@ internal interface IOpenAiEndpointProvider
string CompletionCreate();
string EditCreate();
string ModelsList();
+ string ModelsDelete(string modelId);
string FilesList();
string FilesUpload();
string FileDelete(string fileId);
@@ -17,6 +20,11 @@ internal interface IOpenAiEndpointProvider
string FineTuneCancel(string fineTuneId);
string FineTuneListEvents(string fineTuneId);
string FineTuneDelete(string fineTuneId);
+ string FineTuningJobCreate();
+ string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListRequest);
+ string FineTuningJobRetrieve(string fineTuningJobId);
+ string FineTuningJobCancel(string fineTuningJobId);
+ string FineTuningJobListEvents(string fineTuningJobId);
string EmbeddingCreate();
string ModerationCreate();
string ImageCreate();
diff --git a/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs b/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs
index 40319dbc..d1dfc682 100644
--- a/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs
+++ b/OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs
@@ -1,4 +1,7 @@
-namespace OpenAI.EndpointProviders;
+using System.Net;
+using OpenAI.ObjectModels.RequestModels;
+
+namespace OpenAI.EndpointProviders;
internal class OpenAiEndpointProvider : IOpenAiEndpointProvider
{
@@ -99,6 +102,48 @@ public string FineTuneDelete(string fineTuneId)
return $"/{_apiVersion}/models/{fineTuneId}";
}
+ public string FineTuningJobCreate()
+ {
+ return $"/{_apiVersion}/fine_tuning/jobs";
+ }
+
+ public string FineTuningJobList(FineTuningJobListRequest? fineTuningJobListRequest)
+ {
+ var url = $"/{_apiVersion}/fine_tuning/jobs";
+ if (fineTuningJobListRequest != null)
+ {
+ var queryParams = new List();
+ if (fineTuningJobListRequest.After != null)
+ 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;
+ }
+
+ public string FineTuningJobRetrieve(string fineTuningJobId)
+ {
+ return $"/{_apiVersion}/fine_tuning/jobs/{fineTuningJobId}";
+ }
+
+ public string FineTuningJobCancel(string fineTuningJobId)
+ {
+ return $"/{_apiVersion}/fine_tuning/jobs/{fineTuningJobId}/cancel";
+ }
+
+ public string FineTuningJobListEvents(string fineTuningJobId)
+ {
+ return $"/{_apiVersion}/fine_tuning/jobs/{fineTuningJobId}/events";
+ }
+
+ public string ModelsDelete(string modelId)
+ {
+ return $"/{_apiVersion}/models/{modelId}";
+ }
+
public string EmbeddingCreate()
{
return $"/{_apiVersion}/embeddings";
diff --git a/OpenAI.SDK/Extensions/HttpclientExtensions.cs b/OpenAI.SDK/Extensions/HttpclientExtensions.cs
index 054ed940..ecaa5379 100644
--- a/OpenAI.SDK/Extensions/HttpclientExtensions.cs
+++ b/OpenAI.SDK/Extensions/HttpclientExtensions.cs
@@ -7,7 +7,7 @@ namespace OpenAI.Extensions;
public static class HttpClientExtensions
{
- public static async Task PostAndReadAsAsync(this HttpClient client, string uri, object requestModel, CancellationToken cancellationToken = default)
+ public static async Task PostAndReadAsAsync(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default)
{
var response = await client.PostAsJsonAsync(uri, requestModel, new JsonSerializerOptions
{
diff --git a/OpenAI.SDK/Interfaces/IFineTuningJobService.cs b/OpenAI.SDK/Interfaces/IFineTuningJobService.cs
new file mode 100644
index 00000000..457a0cf2
--- /dev/null
+++ b/OpenAI.SDK/Interfaces/IFineTuningJobService.cs
@@ -0,0 +1,59 @@
+using OpenAI.ObjectModels.RequestModels;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+namespace OpenAI.Interfaces;
+
+///
+/// Manage fine-tuning jobs to tailor a model to your specific training data.
+/// Related guide: Fine-tune models
+///
+public interface IFineTuningJobService
+{
+ ///
+ /// Creates a job that fine-tunes a specified model from a given dataset.
+ /// Response includes details of the enqueued job including job status and the name of the fine-tuned models once
+ /// complete.
+ ///
+ ///
+ /// Propagates notification that operations should be canceled.
+ ///
+ Task CreateFineTuningJob(FineTuningJobCreateRequest createFineTuningJobRequest, CancellationToken cancellationToken = default);
+
+ ///
+ /// List your organization's fine-tuning jobs
+ ///
+ ///
+ /// Propagates notification that operations should be canceled.
+ ///
+ Task ListFineTuningJobs(FineTuningJobListRequest? fineTuningJobListRequest =null,CancellationToken cancellationToken = default);
+
+ ///
+ /// Gets info about the fine-tuning job.
+ ///
+ /// The ID of the fine-tuning job
+ /// Propagates notification that operations should be canceled.
+ ///
+ Task RetrieveFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Immediately cancel a fine-tuning job.
+ ///
+ /// The ID of the fine-tuning job to cancel
+ /// Propagates notification that operations should be canceled.
+ ///
+ Task CancelFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Get fine-grained status updates for a fine-tuning job.
+ ///
+ ///
+ ///
+ /// Whether to stream events for the fine-tuning job. If set to true, events will be sent as data-only server-sent events
+ /// as they become available. The stream will terminate with a data: [DONE] message when the job is finished
+ /// (succeeded, cancelled, or failed).
+ /// If set to false, only events generated so far will be returned.
+ ///
+ /// Propagates notification that operations should be canceled.
+ ///
+ Task ListFineTuningJobEvents(FineTuningJobListEventsRequest model, bool? stream = null, CancellationToken cancellationToken = default);
+}
diff --git a/OpenAI.SDK/Interfaces/IModelService.cs b/OpenAI.SDK/Interfaces/IModelService.cs
index 6c343160..d2c4c6b3 100644
--- a/OpenAI.SDK/Interfaces/IModelService.cs
+++ b/OpenAI.SDK/Interfaces/IModelService.cs
@@ -25,4 +25,12 @@ public interface IModelService
/// Propagates notification that operations should be canceled.
///
Task RetrieveModel(string model, CancellationToken cancellationToken = default);
+
+ ///
+ /// Delete a fine-tuned model. You must have the Owner role in your organization to delete a model.
+ ///
+ /// The model to delete
+ ///
+ ///
+ Task DeleteModel(string modelId, CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/OpenAI.SDK/Interfaces/IOpenAIService.cs b/OpenAI.SDK/Interfaces/IOpenAIService.cs
index dec050e3..671865d4 100644
--- a/OpenAI.SDK/Interfaces/IOpenAIService.cs
+++ b/OpenAI.SDK/Interfaces/IOpenAIService.cs
@@ -28,6 +28,11 @@ public interface IOpenAIService
public IFineTuneService FineTunes { get; }
+ ///
+ /// Manage fine-tuning jobs to tailor a model to your specific training data.
+ ///
+ public IFineTuningJobService FineTuningJob { get; }
+
public IModerationService Moderation { get; }
///
diff --git a/OpenAI.SDK/Managers/OpenAIFineTuningJob.cs b/OpenAI.SDK/Managers/OpenAIFineTuningJob.cs
new file mode 100644
index 00000000..694d05b5
--- /dev/null
+++ b/OpenAI.SDK/Managers/OpenAIFineTuningJob.cs
@@ -0,0 +1,38 @@
+using System.Net;
+using OpenAI.Extensions;
+using OpenAI.Interfaces;
+using OpenAI.ObjectModels.RequestModels;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+using System.Net.Http.Json;
+
+namespace OpenAI.Managers
+{
+ public partial class OpenAIService : IFineTuningJobService
+ {
+ public async Task CreateFineTuningJob(FineTuningJobCreateRequest createFineTuningJobRequest, CancellationToken cancellationToken = default)
+ {
+ return await _httpClient.PostAndReadAsAsync(_endpointProvider.FineTuningJobCreate(), createFineTuningJobRequest, cancellationToken);
+ }
+
+ public async Task ListFineTuningJobs(FineTuningJobListRequest? fineTuningJobListRequest, CancellationToken cancellationToken = default)
+ {
+
+ return (await _httpClient.GetFromJsonAsync(_endpointProvider.FineTuningJobList(fineTuningJobListRequest), cancellationToken))!;
+ }
+
+ public async Task RetrieveFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default)
+ {
+ return (await _httpClient.GetFromJsonAsync(_endpointProvider.FineTuningJobRetrieve(fineTuningJobId), cancellationToken))!;
+ }
+
+ public async Task CancelFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default)
+ {
+ return await _httpClient.PostAndReadAsAsync(_endpointProvider.FineTuningJobCancel(fineTuningJobId),null, cancellationToken: cancellationToken);
+ }
+
+ public async Task ListFineTuningJobEvents(FineTuningJobListEventsRequest fineTuningJobRequestModel, bool? stream = null, CancellationToken cancellationToken = default)
+ {
+ return await _httpClient.GetStreamAsync(_endpointProvider.FineTuningJobListEvents(fineTuningJobRequestModel.FineTuningJobId), cancellationToken);
+ }
+ }
+}
diff --git a/OpenAI.SDK/Managers/OpenAIModel.cs b/OpenAI.SDK/Managers/OpenAIModel.cs
index 9abbc500..7215de3f 100644
--- a/OpenAI.SDK/Managers/OpenAIModel.cs
+++ b/OpenAI.SDK/Managers/OpenAIModel.cs
@@ -15,4 +15,9 @@ public async Task RetrieveModel(string model, Cancellatio
{
return (await _httpClient.GetFromJsonAsync(_endpointProvider.ModelRetrieve(model), cancellationToken))!;
}
+
+ public async Task DeleteModel(string modelId, CancellationToken cancellationToken = default)
+ {
+ return await (await _httpClient.DeleteAsync(_endpointProvider.ModelsDelete(modelId), cancellationToken)).Content.ReadFromJsonAsync(cancellationToken: cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/OpenAI.SDK/Managers/OpenAIService.cs b/OpenAI.SDK/Managers/OpenAIService.cs
index 3a356529..7ed568ae 100644
--- a/OpenAI.SDK/Managers/OpenAIService.cs
+++ b/OpenAI.SDK/Managers/OpenAIService.cs
@@ -5,7 +5,6 @@
namespace OpenAI.Managers;
-//TODO Find a way to show default request values in documentation
public partial class OpenAIService : IOpenAIService, IDisposable
{
private readonly bool _disposeHttpClient;
@@ -85,6 +84,9 @@ public void Dispose()
///
public IFineTuneService FineTunes => this;
+ ///
+ public IFineTuningJobService FineTuningJob => this;
+
///
public IModerationService Moderation => this;
diff --git a/OpenAI.SDK/ObjectModels/RequestModels/FineTuneCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/FineTuneCreateRequest.cs
index c9f0ee89..6e044e1d 100644
--- a/OpenAI.SDK/ObjectModels/RequestModels/FineTuneCreateRequest.cs
+++ b/OpenAI.SDK/ObjectModels/RequestModels/FineTuneCreateRequest.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
using OpenAI.ObjectModels.SharedModels;
namespace OpenAI.ObjectModels.RequestModels;
@@ -37,8 +38,10 @@ public record FineTuneCreateRequest : IOpenAiModels.IModel
///
/// The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ /// Set -1 for "auto value
///
[JsonPropertyName("n_epochs")]
+ [JsonConverter(typeof(NEpochsConverter))]
public int? NEpochs { get; set; }
///
diff --git a/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCancelRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCancelRequest.cs
new file mode 100644
index 00000000..75f7c19d
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCancelRequest.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.RequestModels;
+
+public record FineTuningJobCancelRequest
+{
+ [JsonPropertyName("fine_tuning_job_id")]
+ public string FineTuningJobId { get; set; }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCreateRequest.cs
new file mode 100644
index 00000000..b7fe6d60
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobCreateRequest.cs
@@ -0,0 +1,65 @@
+using System.Text.Json.Serialization;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+using OpenAI.ObjectModels.SharedModels;
+
+namespace OpenAI.ObjectModels.RequestModels;
+
+public record FineTuningJobCreateRequest : IOpenAiModels.IModel
+{
+ ///
+ /// The ID of an uploaded file that contains training data.
+ /// See upload file for how to upload a
+ /// file.
+ /// Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose
+ /// fine-tune
.
+ /// See the fine-tuning guide for more details.
+ ///
+ [JsonPropertyName("training_file")]
+ public string TrainingFile { get; set; }
+
+ ///
+ /// The ID of an uploaded file that contains validation data.
+ /// If you provide this file, the data is used to generate validation metrics periodically during fine-tuning.
+ /// These metrics can be viewed in the fine-tuning results file.
+ /// The same data should not be present in both train and validation files.
+ /// Your dataset must be formatted as a JSONL file. You must upload your file with the purpose fine-tune
.
+ /// See the fine-tuning guide for more details.
+ ///
+ [JsonPropertyName("validation_file")]
+ public string? ValidationFile { get; set; }
+
+ ///
+ /// The hyperparameters used for the fine-tuning job.
+ ///
+ [JsonPropertyName("hyperparameters")]
+ public HyperparametersRequestModel? Hyperparameters { get; set; } // This can be further detailed if the properties of the hyperparameters are known.
+
+ ///
+ /// A string of up to 18 characters that will be added to your fine-tuned model name.
+ /// For example, a suffix
of "custom-model-name" would produce a model name like
+ /// ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel
.
+ ///
+ [JsonPropertyName("suffix")]
+ public string? Suffix { get; set; } = null;
+
+ ///
+ /// The name of the model to fine-tune. You can select one of the
+ /// supported models
+ /// .
+ ///
+ [JsonPropertyName("model")]
+ public string Model { get; set; }
+}
+
+public class HyperparametersRequestModel
+{
+ ///
+ /// The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ /// "Auto" decides the optimal number of epochs based on the size of the dataset. If setting the number manually, we
+ /// support any number between 1 and 50 epochs.
+ /// "Auto" == -1
+ ///
+ [JsonPropertyName("n_epochs")]
+ [JsonConverter(typeof(NEpochsConverter))]
+ public int? NEpochs { get; set; }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobListEventsRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobListEventsRequest.cs
new file mode 100644
index 00000000..e788c1d8
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/RequestModels/FineTuningJobListEventsRequest.cs
@@ -0,0 +1,29 @@
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.RequestModels
+{
+ public class FineTuningJobListEventsRequest:FineTuningJobListRequest
+ {
+ ///
+ /// The ID of the fine-tuning job to get events for.
+ ///
+ [JsonIgnore]
+ public string FineTuningJobId { get; set; }
+ }
+
+ public class FineTuningJobListRequest
+ {
+ ///
+ /// Identifier for the last job from the previous pagination request.
+ ///
+ [JsonPropertyName("after")]
+ public string? After { get; set; }
+
+ ///
+ /// Number of fine-tuning jobs to retrieve.
+ /// Defaults to 20.
+ ///
+ [JsonPropertyName("limit")]
+ public int? Limit { get; set; }
+ }
+}
diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListEventsResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListEventsResponse.cs
new file mode 100644
index 00000000..be4ca12b
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListEventsResponse.cs
@@ -0,0 +1,14 @@
+using OpenAI.ObjectModels.SharedModels;
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+public record FineTuningJobListEventsResponse : BaseResponse
+{
+ [JsonPropertyName("data")]
+ public List Data { get; set; }
+
+ [JsonPropertyName("has_more")]
+ public bool HasMore { get; set; }
+}
+
diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListResponse.cs
new file mode 100644
index 00000000..3f13cd78
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobListResponse.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+public record FineTuningJobListResponse : BaseResponse
+{
+ [JsonPropertyName("data")]
+ public List Data { get; set; }
+
+ [JsonPropertyName("has_more")]
+ public bool HasMore { get; set; }
+}
+
diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs
new file mode 100644
index 00000000..dd50aaa6
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs
@@ -0,0 +1,97 @@
+using System.Text.Json.Serialization;
+using OpenAI.ObjectModels.SharedModels;
+
+namespace OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+public record FineTuningJobResponse : BaseResponse, IOpenAiModels.IId, IOpenAiModels.IModel, IOpenAiModels.ICreatedAt
+{
+ ///
+ /// The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the
+ /// fine-tuning job is still running.
+ ///
+ [JsonPropertyName("finished_at")]
+ public int? FinishedAt { get; set; }
+
+ ///
+ /// The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still
+ /// running.
+ ///
+ [JsonPropertyName("fine_tuned_model")]
+ public string? FineTunedModel { get; set; }
+
+ ///
+ /// The organization that owns the fine-tuning job.
+ ///
+ [JsonPropertyName("organization_id")]
+ public string? OrganizationId { get; set; }
+
+ ///
+ /// The current status of the fine-tuning job, which can be either created, pending, running, succeeded, failed, or
+ /// cancelled.
+ ///
+ [JsonPropertyName("status")]
+ public string Status { get; set; }
+
+ ///
+ /// The hyperparameters used for the fine-tuning job. See the fine-tuning guide for more details.
+ ///
+ [JsonPropertyName("hyperparameters")]
+ public Hyperparameters HyperparametersData { get; set; }
+
+ ///
+ /// The file ID used for training. You can retrieve the training data with the Files API.
+ ///
+ [JsonPropertyName("training_file")]
+ public string TrainingFile { get; set; }
+
+ ///
+ /// The file ID used for validation. You can retrieve the validation results with the Files API.
+ ///
+ [JsonPropertyName("validation_file")]
+ public string? ValidationFile { get; set; }
+
+ ///
+ /// The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the Files API.
+ ///
+ [JsonPropertyName("result_files")]
+ public List ResultFiles { get; set; }
+
+ ///
+ /// The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning
+ /// job is still running.
+ ///
+ [JsonPropertyName("trained_tokens")]
+ public int? TrainedTokens { get; set; }
+
+ ///
+ /// The Unix timestamp (in seconds) for when the fine-tuning job was created.
+ ///
+ [JsonPropertyName("created_at")]
+ public int CreatedAt { get; set; }
+
+ ///
+ /// The object identifier, which can be referenced in the API endpoints.
+ ///
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The base model that is being fine-tuned.
+ ///
+ [JsonPropertyName("model")]
+ public string Model { get; set; }
+
+
+ public class Hyperparameters
+ {
+ ///
+ /// The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ /// "Auto" decides the optimal number of epochs based on the size of the dataset. If setting the number manually, we
+ /// support any number between 1 and 50 epochs.
+ /// "Auto" == -1
+ ///
+ [JsonPropertyName("n_epochs")]
+ [JsonConverter(typeof(NEpochsConverter))]
+ public int? NEpochs { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/NEpochsConverter.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/NEpochsConverter.cs
new file mode 100644
index 00000000..ae88f22a
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/NEpochsConverter.cs
@@ -0,0 +1,42 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
+
+public class NEpochsConverter : JsonConverter
+{
+ public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.String:
+ {
+ var stringValue = reader.GetString();
+ if (stringValue?.ToLower() == "auto")
+ return -1;
+ break;
+ }
+ case JsonTokenType.Number:
+ return reader.GetInt32();
+ case JsonTokenType.Null:
+ return null;
+ }
+
+ throw new JsonException($"Unexpected token type: {reader.TokenType}");
+ }
+
+ public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options)
+ {
+ if (value.HasValue)
+ {
+ if (value.Value == -1)
+ writer.WriteStringValue("auto");
+ else
+ writer.WriteNumberValue(value.Value);
+ }
+ else
+ {
+ writer.WriteNullValue();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/ModelResponseModels/ModelDeleteResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/ModelResponseModels/ModelDeleteResponse.cs
new file mode 100644
index 00000000..4bc146bb
--- /dev/null
+++ b/OpenAI.SDK/ObjectModels/ResponseModels/ModelResponseModels/ModelDeleteResponse.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace OpenAI.ObjectModels.ResponseModels.ModelResponseModels;
+
+public record ModelDeleteResponse : BaseResponse
+{
+ [JsonPropertyName("id")] public string Id { get; set; }
+
+ [JsonPropertyName("deleted")] public bool Deleted { get; set; }
+}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs
index a6e1bbf4..9d74f2e6 100644
--- a/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs
+++ b/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs
@@ -4,11 +4,13 @@ namespace OpenAI.ObjectModels.SharedModels;
public record EventResponse
{
- [JsonPropertyName("object")] public string Object { get; set; }
+ [JsonPropertyName("object")] public string? ObjectTypeName { get; set; }
+ [JsonPropertyName("id")] public string? Id { get; set; }
[JsonPropertyName("created_at")] public int? CreatedAt { get; set; }
[JsonPropertyName("level")] public string Level { get; set; }
[JsonPropertyName("message")] public string Message { get; set; }
+ [JsonPropertyName("type")] public string Type { get; set; }
}
\ No newline at end of file
diff --git a/OpenAI.SDK/ObjectModels/SharedModels/HyperParametersResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/HyperParametersResponse.cs
index ef9f0784..f64ca5a5 100644
--- a/OpenAI.SDK/ObjectModels/SharedModels/HyperParametersResponse.cs
+++ b/OpenAI.SDK/ObjectModels/SharedModels/HyperParametersResponse.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;
namespace OpenAI.ObjectModels.SharedModels;
@@ -9,7 +10,9 @@ public record HyperParametersResponse
[JsonPropertyName("learning_rate_multiplier")]
public float? LearningRateMultiplier { get; set; }
- [JsonPropertyName("n_epochs")] public int? NEpochs { get; set; }
+ [JsonPropertyName("n_epochs")]
+ [JsonConverter(typeof(NEpochsConverter))]
+ public int? NEpochs { get; set; }
[JsonPropertyName("prompt_loss_weight")]
public float? PromptLossWeight { get; set; }
diff --git a/OpenAI.sln.DotSettings b/OpenAI.sln.DotSettings
index cfc5f601..1bc445ce 100644
--- a/OpenAI.sln.DotSettings
+++ b/OpenAI.sln.DotSettings
@@ -5,6 +5,7 @@
True
True
True
+ True
True
True