Skip to content

Commit

Permalink
Merge pull request #368 from aghimir3/opeani-new-fine-tuning-job
Browse files Browse the repository at this point in the history
Add support for new fine tuning job from Open AI
  • Loading branch information
kayhantolga authored Sep 25, 2023
2 parents f74c119 + 43d4f20 commit ad5e7c9
Show file tree
Hide file tree
Showing 26 changed files with 635 additions and 8 deletions.
3 changes: 3 additions & 0 deletions OpenAI.Playground/OpenAI.Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
<None Update="SampleData\FineTuningSample1.jsonl">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="SampleData\FineTuningJobSample2.jsonl">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="SampleData\image_edit_mask.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
4 changes: 2 additions & 2 deletions OpenAI.Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions OpenAI.Playground/SampleData/FineTuningJobSample2.jsonl
Original file line number Diff line number Diff line change
@@ -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."}]}
108 changes: 108 additions & 0 deletions OpenAI.Playground/TestHelpers/FineTuningJobTestHelper.cs
Original file line number Diff line number Diff line change
@@ -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>
{
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);
}
}
}
48 changes: 48 additions & 0 deletions OpenAI.SDK/EndpointProviders/AzureOpenAiEndpointProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using OpenAI.ObjectModels.RequestModels;

namespace OpenAI.EndpointProviders;

Expand Down Expand Up @@ -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<string>();
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}";
Expand Down
10 changes: 9 additions & 1 deletion OpenAI.SDK/EndpointProviders/IOpenAiEndpointProvider.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
namespace OpenAI.EndpointProviders;
using OpenAI.ObjectModels.RequestModels;

namespace OpenAI.EndpointProviders;

internal interface IOpenAiEndpointProvider
{
string ModelRetrieve(string model);
string CompletionCreate();
string EditCreate();
string ModelsList();
string ModelsDelete(string modelId);
string FilesList();
string FilesUpload();
string FileDelete(string fileId);
Expand All @@ -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();
Expand Down
47 changes: 46 additions & 1 deletion OpenAI.SDK/EndpointProviders/OpenAiEndpointProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace OpenAI.EndpointProviders;
using System.Net;
using OpenAI.ObjectModels.RequestModels;

namespace OpenAI.EndpointProviders;

internal class OpenAiEndpointProvider : IOpenAiEndpointProvider
{
Expand Down Expand Up @@ -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<string>();
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";
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.SDK/Extensions/HttpclientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace OpenAI.Extensions;

public static class HttpClientExtensions
{
public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClient client, string uri, object requestModel, CancellationToken cancellationToken = default)
public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default)
{
var response = await client.PostAsJsonAsync(uri, requestModel, new JsonSerializerOptions
{
Expand Down
59 changes: 59 additions & 0 deletions OpenAI.SDK/Interfaces/IFineTuningJobService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels.ResponseModels.FineTuningJobResponseModels;

namespace OpenAI.Interfaces;

/// <summary>
/// Manage fine-tuning jobs to tailor a model to your specific training data.
/// Related guide: <a href="https://platform.openai.com/docs/guides/fine-tuning">Fine-tune models</a>
/// </summary>
public interface IFineTuningJobService
{
/// <summary>
/// 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.
/// </summary>
/// <param name="createFineTuningJobRequest"></param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<FineTuningJobResponse> CreateFineTuningJob(FineTuningJobCreateRequest createFineTuningJobRequest, CancellationToken cancellationToken = default);

/// <summary>
/// List your organization's fine-tuning jobs
/// </summary>
/// <param name="fineTuningJobListRequest"></param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<FineTuningJobListResponse> ListFineTuningJobs(FineTuningJobListRequest? fineTuningJobListRequest =null,CancellationToken cancellationToken = default);

/// <summary>
/// Gets info about the fine-tuning job.
/// </summary>
/// <param name="fineTuningJobId">The ID of the fine-tuning job</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<FineTuningJobResponse> RetrieveFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default);

/// <summary>
/// Immediately cancel a fine-tuning job.
/// </summary>
/// <param name="fineTuningJobId">The ID of the fine-tuning job to cancel</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<FineTuningJobResponse> CancelFineTuningJob(string fineTuningJobId, CancellationToken cancellationToken = default);

/// <summary>
/// Get fine-grained status updates for a fine-tuning job.
/// </summary>
/// <param name="model"></param>
/// <param name="stream">
/// 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.
/// </param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<Stream> ListFineTuningJobEvents(FineTuningJobListEventsRequest model, bool? stream = null, CancellationToken cancellationToken = default);
}
8 changes: 8 additions & 0 deletions OpenAI.SDK/Interfaces/IModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ public interface IModelService
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns></returns>
Task<ModelRetrieveResponse> RetrieveModel(string model, CancellationToken cancellationToken = default);

/// <summary>
/// Delete a fine-tuned model. You must have the Owner role in your organization to delete a model.
/// </summary>
/// <param name="modelId">The model to delete</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<ModelDeleteResponse?> DeleteModel(string modelId, CancellationToken cancellationToken = default);
}
5 changes: 5 additions & 0 deletions OpenAI.SDK/Interfaces/IOpenAIService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public interface IOpenAIService

public IFineTuneService FineTunes { get; }

/// <summary>
/// Manage fine-tuning jobs to tailor a model to your specific training data.
/// </summary>
public IFineTuningJobService FineTuningJob { get; }

public IModerationService Moderation { get; }

/// <summary>
Expand Down
Loading

0 comments on commit ad5e7c9

Please sign in to comment.