diff --git a/.github/workflows/live-test.yml b/.github/workflows/live-test.yml index bd4d5c55..a49aef1b 100644 --- a/.github/workflows/live-test.yml +++ b/.github/workflows/live-test.yml @@ -27,7 +27,7 @@ jobs: - name: Run live tests run: dotnet test ./tests/OpenAI.Tests.csproj --configuration Release - --filter="TestCategory!=Smoke&TestCategory!=Images&TestCategory!=Uploads&TestCategory!=Moderations&TestCategory!=Manual" + --filter="TestCategory!=Smoke&TestCategory!=Images&TestCategory!=Uploads&TestCategory!=Moderations&TestCategory!=FineTuning&TestCategory!=Manual" --logger "trx;LogFilePrefix=live" --results-directory ${{github.workspace}}/artifacts/test-results ${{ env.version_suffix_args}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 815321b7..e20a89e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,11 +45,11 @@ jobs: --filter="TestCategory=Smoke&TestCategory!=Manual" --logger "trx;LogFileName=${{ github.workspace }}/artifacts/test-results/smoke.trx" ${{ env.version_suffix_args }} - + - name: Run Live Tests run: dotnet test ./tests/OpenAI.Tests.csproj --configuration Release - --filter="TestCategory!=Smoke&TestCategory!=Images&TestCategory!=Uploads&TestCategory!=Moderations&TestCategory!=Manual" + --filter="TestCategory!=Smoke&TestCategory!=Images&TestCategory!=Uploads&TestCategory!=Moderations&TestCategory!=FineTuning&TestCategory!=Manual" --logger "trx;LogFilePrefix=live" --results-directory ${{ github.workspace }}/artifacts/test-results ${{ env.version_suffix_args }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c60e3cac..8a28c59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,16 +6,33 @@ ### Breaking Changes -- Refactored `ModerationResult` by merging `ModerationCategories` and `ModerationCategoryScores` into individual `ModerationCategory` properties, each with `Flagged` and `Score` properties. (commit_id) -- Renamed type `OpenAIFileInfo` to `OpenAIFile` and `OpenAIFileInfoCollection` to `OpenAIFileCollection`. (commit_id) -- Renamed type `OpenAIModelInfo` to `OpenAIModel` and `OpenAIModelInfoCollection` to `OpenAIModelCollection`. (commit_id) -- Renamed type `Embedding` to `OpenAIEmbedding` and `EmbeddingCollection` to `OpenAIEmbeddingCollection`. (commit_id) -- Renamed property `ImageUrl` to `ImageUri` and method `FromImageUrl` to `FromImageUri` in the `MessageContent` type. (commit_id) -- Renamed property `ParallelToolCallsEnabled` to `AllowParallelToolCalls` in the `RunCreationOptions`, `ThreadRun`, and `ChatCompletionOptions` types. (commit_id) -- Renamed properties `PromptTokens` to `InputTokenCount`, `CompletionTokens` to `OutputTokenCount`, and `TotalTokens` to `TotalTokenCount` in the `RunTokenUsage` and `RunStepTokenUsage` types. (commit_id) -- Renamed properties `InputTokens` to `InputTokenCount` and `TotalTokens` to `TotalTokenCount` in the `EmbeddingTokenUsage` type. (commit_id) -- Renamed properties `MaxPromptTokens` to `MaxInputTokenCount` and `MaxCompletionTokens` to `MaxOutputTokenCount` in the `ThreadRun`, `RunCreationOptions`, and `RunIncompleteReason` types. (commit_id) -- Removed the `virtual` keyword from the `Pipeline` property across all clients. (commit_id) +- Refactored `ModerationResult` by merging `ModerationCategories` and `ModerationCategoryScores` into individual `ModerationCategory` properties, each with `Flagged` and `Score` properties. (commit_hash) +- Renamed type `OpenAIFileInfo` to `OpenAIFile` and `OpenAIFileInfoCollection` to `OpenAIFileCollection`. (commit_hash) +- Renamed type `OpenAIModelInfo` to `OpenAIModel` and `OpenAIModelInfoCollection` to `OpenAIModelCollection`. (commit_hash) +- Renamed type `Embedding` to `OpenAIEmbedding` and `EmbeddingCollection` to `OpenAIEmbeddingCollection`. (commit_hash) +- Renamed property `ImageUrl` to `ImageUri` and method `FromImageUrl` to `FromImageUri` in the `MessageContent` type. (commit_hash) +- Renamed property `ParallelToolCallsEnabled` to `AllowParallelToolCalls` in the `RunCreationOptions`, `ThreadRun`, and `ChatCompletionOptions` types. (commit_hash) +- Renamed properties `PromptTokens` to `InputTokenCount`, `CompletionTokens` to `OutputTokenCount`, and `TotalTokens` to `TotalTokenCount` in the `RunTokenUsage` and `RunStepTokenUsage` types. (commit_hash) +- Renamed properties `InputTokens` to `InputTokenCount` and `TotalTokens` to `TotalTokenCount` in the `EmbeddingTokenUsage` type. (commit_hash) +- Renamed properties `MaxPromptTokens` to `MaxInputTokenCount` and `MaxCompletionTokens` to `MaxOutputTokenCount` in the `ThreadRun`, `RunCreationOptions`, and `RunIncompleteReason` types. (commit_hash) +- Removed the `virtual` keyword from the `Pipeline` property across all clients. (commit_hash) +- Renamed the `Granularities` property of `AudioTranscriptionOptions` to `TimestampGranularities`. (commit_hash) +- Changed `AudioTranscriptionFormat` from an enum to an "extensible enum". (commit_hash) +- Changed `AudioTranslationFormat` from an enum to an "extensible enum". (commit_hash) +- Changed `GenerateImageFormat` from an enum to an "extensible enum". (commit_hash) +- Changed `GeneratedImageQuality` from an enum to an "extensible enum". (commit_hash) +- Changed `GeneratedImageStyle` from an enum to an "extensible enum". (commit_hash) +- Removed method overloads in `AssistantClient` and `VectorStoreClient` that take complex parameters in favor of methods that take simple string IDs. (commit_hash) +- Updated the `TokenIds` property type in the `TranscribedSegment` type from `IReadOnlyList` to `ReadOnlyMemory`. (commit_hash) +- Updated the `inputs` parameter type in the `GenerateEmbeddings` and `GenerateEmbeddingsAsync` methods of `EmbeddingClient` from `IEnumerable>` to `IEnumerable>`. (commit_hash) +- Changed `ChatMessageContentPartKind` from an extensible enum to an enum. (commit_hash) +- Changed `ChatToolCallKind` from an extensible enum to an enum. (commit_hash) +- Changed `ChatToolKind` from an extensible enum to an enum. (commit_hash) +- Changed `OpenAIFilePurpose` from an extensible enum to an enum. (commit_hash) +- Changed `OpenAIFileStatus` from an extensible enum to an enum. (commit_hash) +- Renamed `OpenAIFilePurpose` to `FilePurpose`. (commit_hash) +- Renamed `OpenAIFileStatus` to `FileStatus`. (commit_hash) +- Removed constructors that take string API key and options. (commit_hash) ### Bugs Fixed diff --git a/README.md b/README.md index d639fe37..c61697db 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena - [How to work with Azure OpenAI](#how-to-work-with-azure-openai) - [Advanced scenarios](#advanced-scenarios) - [Using protocol methods](#using-protocol-methods) + - [Mock a client for testing](#mock-a-client-for-testing) - [Automatically retrying errors](#automatically-retrying-errors) - [Observability](#observability) @@ -129,7 +130,7 @@ foreach (StreamingChatCompletionUpdate update in updates) { foreach (ChatMessageContentPart updatePart in update.ContentUpdate) { - Console.Write(updatePart); + Console.Write(updatePart.Text); } } ``` @@ -309,7 +310,7 @@ To use structured outputs to constrain chat completion content, set an appropria ChatCompletionOptions options = new() { ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat( - name: "math_reasoning", + jsonSchemaFormatName: "math_reasoning", jsonSchema: BinaryData.FromString(""" { "type": "object", @@ -332,7 +333,7 @@ ChatCompletionOptions options = new() "additionalProperties": false } """), - strictSchemaEnabled: true) + jsonSchemaIsStrict: true) }; ChatCompletion chatCompletion = await client.CompleteChatAsync( @@ -340,7 +341,7 @@ ChatCompletion chatCompletion = await client.CompleteChatAsync( options); using JsonDocument structuredJson = JsonDocument.Parse(chatCompletion.ToString()); - + Console.WriteLine($"Final answer: {structuredJson.RootElement.GetProperty("final_answer").GetString()}"); Console.WriteLine("Reasoning steps:"); @@ -360,14 +361,14 @@ To generate a text embedding, use `EmbeddingClient` from the `OpenAI.Embeddings` ```csharp using OpenAI.Embeddings; -EmbeddingClient client = new(model: "text-embedding-3-small", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); +EmbeddingClient client = new("text-embedding-3-small", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); string description = "Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa," + " and a really helpful concierge. The location is perfect -- right downtown, close to all the tourist" + " attractions. We highly recommend this hotel."; -Embedding embedding = client.GenerateEmbedding(description); -ReadOnlyMemory vector = embedding.Vector; +OpenAIEmbedding embedding = client.GenerateEmbedding(description); +ReadOnlyMemory vector = embedding.ToFloats(); ``` Notice that the resulting embedding is a list (also called a vector) of floating point numbers represented as an instance of `ReadOnlyMemory`. By default, the length of the embedding vector will be 1536 when using the `text-embedding-3-small` model or 3072 when using the `text-embedding-3-large` model. Generally, larger embeddings perform better, but using them also tends to cost more in terms of compute, memory, and storage. You can reduce the dimensions of the embedding by creating an instance of the `EmbeddingGenerationOptions` class, setting the `Dimensions` property, and passing it as an argument in your call to the `GenerateEmbedding` method: @@ -375,7 +376,7 @@ Notice that the resulting embedding is a list (also called a vector) of floating ```csharp EmbeddingGenerationOptions options = new() { Dimensions = 512 }; -Embedding embedding = client.GenerateEmbedding(description, options); +OpenAIEmbedding embedding = client.GenerateEmbedding(description, options); ``` ## How to generate images @@ -387,7 +388,7 @@ To generate an image, use `ImageClient` from the `OpenAI.Images` namespace: ```csharp using OpenAI.Images; -ImageClient client = new(model: "dall-e-3", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); +ImageClient client = new("dall-e-3", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); ``` Generating an image always requires a `prompt` that describes what should be generated. To further tailor the image generation to your specific needs, you can create an instance of the `ImageGenerationOptions` class and set the `Quality`, `Size`, and `Style` properties accordingly. Note that you can also set the `ResponseFormat` property of `ImageGenerationOptions` to `GeneratedImageFormat.Bytes` in order to receive the resulting PNG as `BinaryData` (instead of the default remote `Uri`) if this is convenient for your use case. @@ -431,14 +432,14 @@ In this example, an audio file is transcribed using the Whisper speech-to-text m ```csharp using OpenAI.Audio; -AudioClient client = new(model: "whisper-1", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); +AudioClient client = new("whisper-1", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); string audioFilePath = Path.Combine("Assets", "audio_houseplant_care.mp3"); AudioTranscriptionOptions options = new() { ResponseFormat = AudioTranscriptionFormat.Verbose, - Granularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, + TimestampGranularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, }; AudioTranscription transcription = client.TranscribeAudio(audioFilePath, options); @@ -450,14 +451,14 @@ Console.WriteLine(); Console.WriteLine($"Words:"); foreach (TranscribedWord word in transcription.Words) { - Console.WriteLine($" {word.Word,15} : {word.Start.TotalMilliseconds,5:0} - {word.End.TotalMilliseconds,5:0}"); + Console.WriteLine($" {word.Word,15} : {word.StartTime.TotalMilliseconds,5:0} - {word.EndTime.TotalMilliseconds,5:0}"); } Console.WriteLine(); Console.WriteLine($"Segments:"); foreach (TranscribedSegment segment in transcription.Segments) { - Console.WriteLine($" {segment.Text,90} : {segment.Start.TotalMilliseconds,5:0} - {segment.End.TotalMilliseconds,5:0}"); + Console.WriteLine($" {segment.Text,90} : {segment.StartTime.TotalMilliseconds,5:0} - {segment.EndTime.TotalMilliseconds,5:0}"); } ``` @@ -516,7 +517,7 @@ using Stream document = BinaryData.FromString(""" Upload this document to OpenAI using the `FileClient`'s `UploadFile` method, ensuring that you use `FileUploadPurpose.Assistants` to allow your assistant to access it later: ```csharp -OpenAIFileInfo salesFile = fileClient.UploadFile( +OpenAIFile salesFile = fileClient.UploadFile( document, "monthly_sales.json", FileUploadPurpose.Assistants); @@ -584,8 +585,8 @@ Finally, you can use the `AssistantClient`'s `GetMessages` method to retrieve th For illustrative purposes, you could print the messages to the console and also save any images produced by the assistant to local storage: ```csharp -PageCollection messagePages = assistantClient.GetMessages(threadRun.ThreadId, new MessageCollectionOptions() { Order = ListOrder.OldestFirst }); -IEnumerable messages = messagePages.GetAllValues(); +CollectionResult messages + = assistantClient.GetMessages(threadRun.ThreadId, new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending }); foreach (ThreadMessage message in messages) { @@ -616,7 +617,7 @@ foreach (ThreadMessage message in messages) } if (!string.IsNullOrEmpty(contentItem.ImageFileId)) { - OpenAIFileInfo imageInfo = fileClient.GetFile(contentItem.ImageFileId); + OpenAIFile imageInfo = fileClient.GetFile(contentItem.ImageFileId); BinaryData imageBytes = fileClient.DownloadFile(contentItem.ImageFileId); using FileStream stream = File.OpenWrite($"{imageInfo.Filename}.png"); imageBytes.ToStream().CopyTo(stream); @@ -666,8 +667,8 @@ AssistantClient assistantClient = openAIClient.GetAssistantClient(); For this example, we will use both image data from a local file as well as an image located at a URL. For the local data, we upload the file with the `Vision` upload purpose, which would also allow it to be downloaded and retrieved later. ```csharp -OpenAIFileInfo pictureOfAppleFile = fileClient.UploadFile( - "picture-of-apple.jpg", +OpenAIFile pictureOfAppleFile = fileClient.UploadFile( + Path.Combine("Assets", "picture-of-apple.png"), FileUploadPurpose.Vision); Uri linkToPictureOfOrange = new("https://platform.openai.com/fictitious-files/picture-of-orange.png"); ``` @@ -676,7 +677,7 @@ Next, create a new assistant with a vision-capable model like `gpt-4o` and a thr ```csharp Assistant assistant = assistantClient.CreateAssistant( - model: "gpt-4o", + "gpt-4o", new AssistantCreationOptions() { Instructions = "When asked a question, attempt to answer very concisely. " @@ -686,14 +687,15 @@ Assistant assistant = assistantClient.CreateAssistant( AssistantThread thread = assistantClient.CreateThread(new ThreadCreationOptions() { InitialMessages = - { - new ThreadInitializationMessage( - [ - "Hello, assistant! Please compare these two images for me:", - MessageContent.FromImageFileId(pictureOfAppleFile.Id), - MessageContent.FromImageUrl(linkToPictureOfOrange), - ]), - } + { + new ThreadInitializationMessage( + MessageRole.User, + [ + "Hello, assistant! Please compare these two images for me:", + MessageContent.FromImageFileId(pictureOfAppleFile.Id), + MessageContent.FromImageUri(linkToPictureOfOrange), + ]), + } }); ``` @@ -701,8 +703,8 @@ With the assistant and thread prepared, use the `CreateRunStreaming` method to g ```csharp CollectionResult streamingUpdates = assistantClient.CreateRunStreaming( - thread, - assistant, + thread.Id, + assistant.Id, new RunCreationOptions() { AdditionalInstructions = "When possible, try to sneak in puns if you're asked to compare things.", @@ -795,6 +797,52 @@ string message = outputAsJson.RootElement Notice how you can then call the resulting `ClientResult`'s `GetRawResponse` method and retrieve the response body as `BinaryData` via the `PipelineResponse`'s `Content` property. +### Mock a client for testing + +The OpenAI .NET library has been designed to support mocking, providing key features such as: +- Client methods made virtual to allow overriding. +- Model factories to assist in instantiating API output models that lack public constructors. + +To illustrate how mocking works, suppose you want to validate the behavior of the following method using the [Moq](https://github.com/devlooped/moq) library. Given the path to an audio file, it determines whether it contains a specified secret word: + +```csharp +public bool ContainsSecretWord(AudioClient client, string audioFilePath, string secretWord) +{ + AudioTranscription transcription = client.TranscribeAudio(audioFilePath); + return transcription.Text.Contains(secretWord); +} +``` + +Create mocks of `AudioClient` and `ClientResult`, set up methods and properties that will be invoked, then test the behavior of the `ContainsSecretWord` method. Since the `AudioTranscription` class does not provide public constructors, it must be instantiated by the `OpenAIAudioModelFactory` static class: + +```csharp +// Instantiate mocks and the AudioTranscription object. + +Mock mockClient = new(); +Mock> mockResult = new(null, Mock.Of()); +AudioTranscription transcription = OpenAIAudioModelFactory.AudioTranscription(text: "I swear I saw an apple flying yesterday!"); + +// Set up mocks' properties and methods. + +mockResult + .SetupGet(result => result.Value) + .Returns(transcription); + +mockClient.Setup(client => client.TranscribeAudio( + It.IsAny(), + It.IsAny())) + .Returns(mockResult.Object); + +// Perform validation. + +AudioClient client = mockClient.Object; +bool containsSecretWord = ContainsSecretWord(client, "", "apple"); + +Assert.That(containsSecretWord, Is.True); +``` + +All namespaces have their corresponding model factory to support mocking with the exception of the `OpenAI.Assistants` and `OpenAI.VectorStores` namespaces, for which model factories are coming soon. + ### Automatically retrying errors By default, the client classes will automatically retry the following errors up to three additional times using exponential backoff: diff --git a/api/OpenAI.netstandard2.0.cs b/api/OpenAI.netstandard2.0.cs index 421ec983..f3162139 100644 --- a/api/OpenAI.netstandard2.0.cs +++ b/api/OpenAI.netstandard2.0.cs @@ -4,7 +4,6 @@ public class OpenAIClient { public OpenAIClient(ApiKeyCredential credential, OpenAIClientOptions options); public OpenAIClient(ApiKeyCredential credential); protected internal OpenAIClient(ClientPipeline pipeline, OpenAIClientOptions options); - public OpenAIClient(string apiKey, OpenAIClientOptions options); public OpenAIClient(string apiKey); public ClientPipeline Pipeline { get; } public virtual AssistantClient GetAssistantClient(); @@ -51,14 +50,11 @@ public class AssistantClient { public AssistantClient(ApiKeyCredential credential, OpenAIClientOptions options); public AssistantClient(ApiKeyCredential credential); protected internal AssistantClient(ClientPipeline pipeline, OpenAIClientOptions options); - public AssistantClient(string apiKey, OpenAIClientOptions options); public AssistantClient(string apiKey); public ClientPipeline Pipeline { get; } - public virtual ClientResult CancelRun(ThreadRun run); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CancelRun(string threadId, string runId, RequestOptions options); public virtual ClientResult CancelRun(string threadId, string runId, CancellationToken cancellationToken = default); - public virtual Task> CancelRunAsync(ThreadRun run); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CancelRunAsync(string threadId, string runId, RequestOptions options); public virtual Task> CancelRunAsync(string threadId, string runId, CancellationToken cancellationToken = default); @@ -68,65 +64,49 @@ public class AssistantClient { [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CreateAssistantAsync(BinaryContent content, RequestOptions options = null); public virtual Task> CreateAssistantAsync(string model, AssistantCreationOptions options = null, CancellationToken cancellationToken = default); - public virtual ClientResult CreateMessage(AssistantThread thread, MessageRole role, IEnumerable content, MessageCreationOptions options = null); public virtual ClientResult CreateMessage(string threadId, MessageRole role, IEnumerable content, MessageCreationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CreateMessage(string threadId, BinaryContent content, RequestOptions options = null); - public virtual Task> CreateMessageAsync(AssistantThread thread, MessageRole role, IEnumerable content, MessageCreationOptions options = null); public virtual Task> CreateMessageAsync(string threadId, MessageRole role, IEnumerable content, MessageCreationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CreateMessageAsync(string threadId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult CreateRun(AssistantThread thread, Assistant assistant, RunCreationOptions options = null); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CreateRun(string threadId, BinaryContent content, RequestOptions options = null); public virtual ClientResult CreateRun(string threadId, string assistantId, RunCreationOptions options = null, CancellationToken cancellationToken = default); - public virtual Task> CreateRunAsync(AssistantThread thread, Assistant assistant, RunCreationOptions options = null); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CreateRunAsync(string threadId, BinaryContent content, RequestOptions options = null); public virtual Task> CreateRunAsync(string threadId, string assistantId, RunCreationOptions options = null, CancellationToken cancellationToken = default); - public virtual CollectionResult CreateRunStreaming(AssistantThread thread, Assistant assistant, RunCreationOptions options = null); public virtual CollectionResult CreateRunStreaming(string threadId, string assistantId, RunCreationOptions options = null, CancellationToken cancellationToken = default); - public virtual AsyncCollectionResult CreateRunStreamingAsync(AssistantThread thread, Assistant assistant, RunCreationOptions options = null); public virtual AsyncCollectionResult CreateRunStreamingAsync(string threadId, string assistantId, RunCreationOptions options = null, CancellationToken cancellationToken = default); public virtual ClientResult CreateThread(ThreadCreationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CreateThread(BinaryContent content, RequestOptions options = null); - public virtual ClientResult CreateThreadAndRun(Assistant assistant, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CreateThreadAndRun(BinaryContent content, RequestOptions options = null); public virtual ClientResult CreateThreadAndRun(string assistantId, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null, CancellationToken cancellationToken = default); - public virtual Task> CreateThreadAndRunAsync(Assistant assistant, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CreateThreadAndRunAsync(BinaryContent content, RequestOptions options = null); public virtual Task> CreateThreadAndRunAsync(string assistantId, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null, CancellationToken cancellationToken = default); - public virtual CollectionResult CreateThreadAndRunStreaming(Assistant assistant, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null); public virtual CollectionResult CreateThreadAndRunStreaming(string assistantId, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null, CancellationToken cancellationToken = default); - public virtual AsyncCollectionResult CreateThreadAndRunStreamingAsync(Assistant assistant, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null); public virtual AsyncCollectionResult CreateThreadAndRunStreamingAsync(string assistantId, ThreadCreationOptions threadOptions = null, RunCreationOptions runOptions = null, CancellationToken cancellationToken = default); public virtual Task> CreateThreadAsync(ThreadCreationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CreateThreadAsync(BinaryContent content, RequestOptions options = null); - public virtual ClientResult DeleteAssistant(Assistant assistant); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult DeleteAssistant(string assistantId, RequestOptions options); public virtual ClientResult DeleteAssistant(string assistantId, CancellationToken cancellationToken = default); - public virtual Task> DeleteAssistantAsync(Assistant assistant); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task DeleteAssistantAsync(string assistantId, RequestOptions options); public virtual Task> DeleteAssistantAsync(string assistantId, CancellationToken cancellationToken = default); - public virtual ClientResult DeleteMessage(ThreadMessage message); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult DeleteMessage(string threadId, string messageId, RequestOptions options); public virtual ClientResult DeleteMessage(string threadId, string messageId, CancellationToken cancellationToken = default); - public virtual Task> DeleteMessageAsync(ThreadMessage message); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task DeleteMessageAsync(string threadId, string messageId, RequestOptions options); public virtual Task> DeleteMessageAsync(string threadId, string messageId, CancellationToken cancellationToken = default); - public virtual ClientResult DeleteThread(AssistantThread thread); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult DeleteThread(string threadId, RequestOptions options); public virtual ClientResult DeleteThread(string threadId, CancellationToken cancellationToken = default); - public virtual Task> DeleteThreadAsync(AssistantThread thread); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task DeleteThreadAsync(string threadId, RequestOptions options); public virtual Task> DeleteThreadAsync(string threadId, CancellationToken cancellationToken = default); @@ -144,38 +124,30 @@ public class AssistantClient { public virtual AsyncCollectionResult GetAssistantsAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual AsyncCollectionResult GetAssistantsAsync(int? limit, string order, string after, string before, RequestOptions options); - public virtual ClientResult GetMessage(ThreadMessage message); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GetMessage(string threadId, string messageId, RequestOptions options); public virtual ClientResult GetMessage(string threadId, string messageId, CancellationToken cancellationToken = default); - public virtual Task> GetMessageAsync(ThreadMessage message); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetMessageAsync(string threadId, string messageId, RequestOptions options); public virtual Task> GetMessageAsync(string threadId, string messageId, CancellationToken cancellationToken = default); - public virtual CollectionResult GetMessages(AssistantThread thread, MessageCollectionOptions options = null); public virtual CollectionResult GetMessages(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual CollectionResult GetMessages(string threadId, MessageCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual CollectionResult GetMessages(string threadId, int? limit, string order, string after, string before, RequestOptions options); - public virtual AsyncCollectionResult GetMessagesAsync(AssistantThread thread, MessageCollectionOptions options = null); public virtual AsyncCollectionResult GetMessagesAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual AsyncCollectionResult GetMessagesAsync(string threadId, MessageCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual AsyncCollectionResult GetMessagesAsync(string threadId, int? limit, string order, string after, string before, RequestOptions options); - public virtual ClientResult GetRun(ThreadRun run); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GetRun(string threadId, string runId, RequestOptions options); public virtual ClientResult GetRun(string threadId, string runId, CancellationToken cancellationToken = default); - public virtual Task> GetRunAsync(ThreadRun run); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetRunAsync(string threadId, string runId, RequestOptions options); public virtual Task> GetRunAsync(string threadId, string runId, CancellationToken cancellationToken = default); - public virtual CollectionResult GetRuns(AssistantThread thread, RunCollectionOptions options = null); public virtual CollectionResult GetRuns(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual CollectionResult GetRuns(string threadId, RunCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual CollectionResult GetRuns(string threadId, int? limit, string order, string after, string before, RequestOptions options); - public virtual AsyncCollectionResult GetRunsAsync(AssistantThread thread, RunCollectionOptions options = null); public virtual AsyncCollectionResult GetRunsAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual AsyncCollectionResult GetRunsAsync(string threadId, RunCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] @@ -186,37 +158,29 @@ public class AssistantClient { [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetRunStepAsync(string threadId, string runId, string stepId, RequestOptions options); public virtual Task> GetRunStepAsync(string threadId, string runId, string stepId, CancellationToken cancellationToken = default); - public virtual CollectionResult GetRunSteps(ThreadRun run, RunStepCollectionOptions options = null); public virtual CollectionResult GetRunSteps(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual CollectionResult GetRunSteps(string threadId, string runId, RunStepCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual CollectionResult GetRunSteps(string threadId, string runId, int? limit, string order, string after, string before, RequestOptions options); - public virtual AsyncCollectionResult GetRunStepsAsync(ThreadRun run, RunStepCollectionOptions options = null); public virtual AsyncCollectionResult GetRunStepsAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual AsyncCollectionResult GetRunStepsAsync(string threadId, string runId, RunStepCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual AsyncCollectionResult GetRunStepsAsync(string threadId, string runId, int? limit, string order, string after, string before, RequestOptions options); - public virtual ClientResult GetThread(AssistantThread thread); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GetThread(string threadId, RequestOptions options); public virtual ClientResult GetThread(string threadId, CancellationToken cancellationToken = default); - public virtual Task> GetThreadAsync(AssistantThread thread); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetThreadAsync(string threadId, RequestOptions options); public virtual Task> GetThreadAsync(string threadId, CancellationToken cancellationToken = default); - public virtual ClientResult ModifyAssistant(Assistant assistant, AssistantModificationOptions options); public virtual ClientResult ModifyAssistant(string assistantId, AssistantModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult ModifyAssistant(string assistantId, BinaryContent content, RequestOptions options = null); - public virtual Task> ModifyAssistantAsync(Assistant assistant, AssistantModificationOptions options); public virtual Task> ModifyAssistantAsync(string assistantId, AssistantModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task ModifyAssistantAsync(string assistantId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult ModifyMessage(ThreadMessage message, MessageModificationOptions options); public virtual ClientResult ModifyMessage(string threadId, string messageId, MessageModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult ModifyMessage(string threadId, string messageId, BinaryContent content, RequestOptions options = null); - public virtual Task> ModifyMessageAsync(ThreadMessage message, MessageModificationOptions options); public virtual Task> ModifyMessageAsync(string threadId, string messageId, MessageModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task ModifyMessageAsync(string threadId, string messageId, BinaryContent content, RequestOptions options = null); @@ -224,25 +188,19 @@ public class AssistantClient { public virtual ClientResult ModifyRun(string threadId, string runId, BinaryContent content, RequestOptions options = null); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task ModifyRunAsync(string threadId, string runId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult ModifyThread(AssistantThread thread, ThreadModificationOptions options); public virtual ClientResult ModifyThread(string threadId, ThreadModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult ModifyThread(string threadId, BinaryContent content, RequestOptions options = null); - public virtual Task> ModifyThreadAsync(AssistantThread thread, ThreadModificationOptions options); public virtual Task> ModifyThreadAsync(string threadId, ThreadModificationOptions options, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task ModifyThreadAsync(string threadId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult SubmitToolOutputsToRun(ThreadRun run, IEnumerable toolOutputs); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult SubmitToolOutputsToRun(string threadId, string runId, BinaryContent content, RequestOptions options = null); public virtual ClientResult SubmitToolOutputsToRun(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default); - public virtual Task> SubmitToolOutputsToRunAsync(ThreadRun run, IEnumerable toolOutputs); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task SubmitToolOutputsToRunAsync(string threadId, string runId, BinaryContent content, RequestOptions options = null); public virtual Task> SubmitToolOutputsToRunAsync(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default); - public virtual CollectionResult SubmitToolOutputsToRunStreaming(ThreadRun run, IEnumerable toolOutputs); public virtual CollectionResult SubmitToolOutputsToRunStreaming(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default); - public virtual AsyncCollectionResult SubmitToolOutputsToRunStreamingAsync(ThreadRun run, IEnumerable toolOutputs); public virtual AsyncCollectionResult SubmitToolOutputsToRunStreamingAsync(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default); } public class AssistantCollectionOptions { @@ -1112,7 +1070,6 @@ public class AudioClient { protected internal AudioClient(ClientPipeline pipeline, string model, OpenAIClientOptions options); public AudioClient(string model, ApiKeyCredential credential, OpenAIClientOptions options); public AudioClient(string model, ApiKeyCredential credential); - public AudioClient(string model, string apiKey, OpenAIClientOptions options); public AudioClient(string model, string apiKey); public ClientPipeline Pipeline { get; } [EditorBrowsable(EditorBrowsableState.Never)] @@ -1154,19 +1111,32 @@ public class AudioTranscription : IJsonModel, IPersistableMo string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public enum AudioTranscriptionFormat { - Text = 0, - Simple = 1, - Verbose = 2, - Srt = 3, - Vtt = 4 + public readonly partial struct AudioTranscriptionFormat : IEquatable { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public AudioTranscriptionFormat(string value); + public static AudioTranscriptionFormat Simple { get; } + public static AudioTranscriptionFormat Srt { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public static AudioTranscriptionFormat Text { get; } + public static AudioTranscriptionFormat Verbose { get; } + public static AudioTranscriptionFormat Vtt { get; } + public readonly bool Equals(AudioTranscriptionFormat other); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode(); + public static bool operator ==(AudioTranscriptionFormat left, AudioTranscriptionFormat right); + public static implicit operator AudioTranscriptionFormat(string value); + public static bool operator !=(AudioTranscriptionFormat left, AudioTranscriptionFormat right); + public override readonly string ToString(); } public class AudioTranscriptionOptions : IJsonModel, IPersistableModel { - public AudioTimestampGranularities Granularities { get; set; } public string Language { get; set; } public string Prompt { get; set; } public AudioTranscriptionFormat? ResponseFormat { get; set; } public float? Temperature { get; set; } + public AudioTimestampGranularities TimestampGranularities { get; set; } AudioTranscriptionOptions IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options); void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options); AudioTranscriptionOptions IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options); @@ -1184,12 +1154,25 @@ public class AudioTranslation : IJsonModel, IPersistableModel< string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public enum AudioTranslationFormat { - Text = 0, - Simple = 1, - Verbose = 2, - Srt = 3, - Vtt = 4 + public readonly partial struct AudioTranslationFormat : IEquatable { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public AudioTranslationFormat(string value); + public static AudioTranslationFormat Simple { get; } + public static AudioTranslationFormat Srt { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public static AudioTranslationFormat Text { get; } + public static AudioTranslationFormat Verbose { get; } + public static AudioTranslationFormat Vtt { get; } + public readonly bool Equals(AudioTranslationFormat other); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode(); + public static bool operator ==(AudioTranslationFormat left, AudioTranslationFormat right); + public static implicit operator AudioTranslationFormat(string value); + public static bool operator !=(AudioTranslationFormat left, AudioTranslationFormat right); + public override readonly string ToString(); } public class AudioTranslationOptions : IJsonModel, IPersistableModel { public string Prompt { get; set; } @@ -1244,7 +1227,7 @@ public class AudioTranslationOptions : IJsonModel, IPer public static class OpenAIAudioModelFactory { public static AudioTranscription AudioTranscription(string language = null, TimeSpan? duration = null, string text = null, IEnumerable words = null, IEnumerable segments = null); public static AudioTranslation AudioTranslation(string language = null, TimeSpan? duration = null, string text = null, IEnumerable segments = null); - public static TranscribedSegment TranscribedSegment(int id = 0, int seekOffset = 0, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, IEnumerable tokenIds = null, float temperature = 0, float averageLogProbability = 0, float compressionRatio = 0, float noSpeechProbability = 0); + public static TranscribedSegment TranscribedSegment(int id = 0, int seekOffset = 0, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, ReadOnlyMemory tokenIds = default, float temperature = 0, float averageLogProbability = 0, float compressionRatio = 0, float noSpeechProbability = 0); public static TranscribedWord TranscribedWord(string word = null, TimeSpan startTime = default, TimeSpan endTime = default); } public class SpeechGenerationOptions : IJsonModel, IPersistableModel { @@ -1268,7 +1251,7 @@ public class SpeechGenerationOptions : IJsonModel, IPer public TimeSpan StartTime { get; } public float Temperature { get; } public string Text { get; } - public IReadOnlyList TokenIds { get; } + public ReadOnlyMemory TokenIds { get; } readonly TranscribedSegment IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options); readonly void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options); readonly object IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options); @@ -1304,22 +1287,30 @@ public class BatchClient { public BatchClient(ApiKeyCredential credential, OpenAIClientOptions options); public BatchClient(ApiKeyCredential credential); protected internal BatchClient(ClientPipeline pipeline, OpenAIClientOptions options); - public BatchClient(string apiKey, OpenAIClientOptions options); public BatchClient(string apiKey); public ClientPipeline Pipeline { get; } - public virtual ClientResult CancelBatch(string batchId, RequestOptions options); - public virtual Task CancelBatchAsync(string batchId, RequestOptions options); - public virtual ClientResult CreateBatch(BinaryContent content, RequestOptions options = null); - public virtual Task CreateBatchAsync(BinaryContent content, RequestOptions options = null); - public virtual ClientResult GetBatch(string batchId, RequestOptions options); - public virtual Task GetBatchAsync(string batchId, RequestOptions options); + public virtual CreateBatchOperation CreateBatch(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual Task CreateBatchAsync(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); public virtual CollectionResult GetBatches(string after, int? limit, RequestOptions options); public virtual AsyncCollectionResult GetBatchesAsync(string after, int? limit, RequestOptions options); } + public class CreateBatchOperation : OperationResult { + public string BatchId { get; } + public override ContinuationToken? RehydrationToken { get; protected set; } + public virtual ClientResult Cancel(RequestOptions? options); + public virtual Task CancelAsync(RequestOptions? options); + public virtual ClientResult GetBatch(RequestOptions? options); + public virtual Task GetBatchAsync(RequestOptions? options); + public static CreateBatchOperation Rehydrate(BatchClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(BatchClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public override ClientResult UpdateStatus(RequestOptions? options = null); + public override ValueTask UpdateStatusAsync(RequestOptions? options = null); + } } namespace OpenAI.Chat { public class AssistantChatMessage : ChatMessage, IJsonModel, IPersistableModel { public AssistantChatMessage(ChatCompletion chatCompletion); + [Obsolete("This constructor is obsolete. Please use the constructor that takes an IEnumerable parameter instead.")] public AssistantChatMessage(ChatFunctionCall functionCall); public AssistantChatMessage(params ChatMessageContentPart[] contentParts); public AssistantChatMessage(IEnumerable contentParts); @@ -1341,7 +1332,6 @@ public class ChatClient { protected internal ChatClient(ClientPipeline pipeline, string model, OpenAIClientOptions options); public ChatClient(string model, ApiKeyCredential credential, OpenAIClientOptions options); public ChatClient(string model, ApiKeyCredential credential); - public ChatClient(string model, string apiKey, OpenAIClientOptions options); public ChatClient(string model, string apiKey); public ClientPipeline Pipeline { get; } public virtual ClientResult CompleteChat(params ChatMessage[] messages); @@ -1463,7 +1453,7 @@ public class ChatFunctionChoice : IJsonModel, IPersistableMo public static bool operator !=(ChatImageDetailLevel left, ChatImageDetailLevel right); public override readonly string ToString(); } - public abstract class ChatMessage : IJsonModel, IPersistableModel { + public class ChatMessage : IJsonModel, IPersistableModel { public IList Content { get; } public static AssistantChatMessage CreateAssistantMessage(ChatCompletion chatCompletion); public static AssistantChatMessage CreateAssistantMessage(ChatFunctionCall functionCall); @@ -1508,22 +1498,10 @@ public class ChatMessageContentPart : IJsonModel, IPersi string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public readonly partial struct ChatMessageContentPartKind : IEquatable { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public ChatMessageContentPartKind(string value); - public static ChatMessageContentPartKind Image { get; } - public static ChatMessageContentPartKind Refusal { get; } - public static ChatMessageContentPartKind Text { get; } - public readonly bool Equals(ChatMessageContentPartKind other); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly bool Equals(object obj); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly int GetHashCode(); - public static bool operator ==(ChatMessageContentPartKind left, ChatMessageContentPartKind right); - public static implicit operator ChatMessageContentPartKind(string value); - public static bool operator !=(ChatMessageContentPartKind left, ChatMessageContentPartKind right); - public override readonly string ToString(); + public enum ChatMessageContentPartKind { + Text = 0, + Refusal = 1, + Image = 2 } public enum ChatMessageRole { System = 0, @@ -1540,7 +1518,7 @@ public class ChatOutputTokenUsageDetails : IJsonModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public abstract class ChatResponseFormat : IJsonModel, IPersistableModel { + public class ChatResponseFormat : IJsonModel, IPersistableModel { public static ChatResponseFormat CreateJsonObjectFormat(); public static ChatResponseFormat CreateJsonSchemaFormat(string jsonSchemaFormatName, BinaryData jsonSchema, string jsonSchemaFormatDescription = null, bool? jsonSchemaIsStrict = null); public static ChatResponseFormat CreateTextFormat(); @@ -1607,20 +1585,8 @@ public class ChatToolCall : IJsonModel, IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public readonly partial struct ChatToolCallKind : IEquatable { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public ChatToolCallKind(string value); - public static ChatToolCallKind Function { get; } - public readonly bool Equals(ChatToolCallKind other); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly bool Equals(object obj); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly int GetHashCode(); - public static bool operator ==(ChatToolCallKind left, ChatToolCallKind right); - public static implicit operator ChatToolCallKind(string value); - public static bool operator !=(ChatToolCallKind left, ChatToolCallKind right); - public override readonly string ToString(); + public enum ChatToolCallKind { + Function = 0 } public class ChatToolChoice : IJsonModel, IPersistableModel { public static ChatToolChoice CreateAutoChoice(); @@ -1633,20 +1599,8 @@ public class ChatToolChoice : IJsonModel, IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public readonly partial struct ChatToolKind : IEquatable { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public ChatToolKind(string value); - public static ChatToolKind Function { get; } - public readonly bool Equals(ChatToolKind other); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly bool Equals(object obj); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly int GetHashCode(); - public static bool operator ==(ChatToolKind left, ChatToolKind right); - public static implicit operator ChatToolKind(string value); - public static bool operator !=(ChatToolKind left, ChatToolKind right); - public override readonly string ToString(); + public enum ChatToolKind { + Function = 0 } [Obsolete("This class is obsolete. Please use ToolChatMessage instead.")] public class FunctionChatMessage : ChatMessage, IJsonModel, IPersistableModel { @@ -1667,7 +1621,7 @@ public static class OpenAIChatModelFactory { public static StreamingChatCompletionUpdate StreamingChatCompletionUpdate(string id = null, IEnumerable contentUpdate = null, StreamingChatFunctionCallUpdate functionCallUpdate = null, IEnumerable toolCallUpdates = null, ChatMessageRole? role = null, string refusalUpdate = null, IEnumerable contentTokenLogProbabilities = null, IEnumerable refusalTokenLogProbabilities = null, ChatFinishReason? finishReason = null, DateTimeOffset createdAt = default, string model = null, string systemFingerprint = null, ChatTokenUsage usage = null); [Obsolete("This class is obsolete. Please use StreamingChatToolCallUpdate instead.")] public static StreamingChatFunctionCallUpdate StreamingChatFunctionCallUpdate(string functionArgumentsUpdate = null, string functionName = null); - public static StreamingChatToolCallUpdate StreamingChatToolCallUpdate(int index = 0, string id = null, ChatToolCallKind kind = default, string functionName = null, string functionArgumentsUpdate = null); + public static StreamingChatToolCallUpdate StreamingChatToolCallUpdate(int index = 0, string id = null, ChatToolCallKind kind = ChatToolCallKind.Function, string functionName = null, string functionArgumentsUpdate = null); } public class StreamingChatCompletionUpdate : IJsonModel, IPersistableModel { public IReadOnlyList ContentTokenLogProbabilities { get; } @@ -1752,18 +1706,17 @@ public class EmbeddingClient { protected internal EmbeddingClient(ClientPipeline pipeline, string model, OpenAIClientOptions options); public EmbeddingClient(string model, ApiKeyCredential credential, OpenAIClientOptions options); public EmbeddingClient(string model, ApiKeyCredential credential); - public EmbeddingClient(string model, string apiKey, OpenAIClientOptions options); public EmbeddingClient(string model, string apiKey); public ClientPipeline Pipeline { get; } public virtual ClientResult GenerateEmbedding(string input, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); public virtual Task> GenerateEmbeddingAsync(string input, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GenerateEmbeddings(BinaryContent content, RequestOptions options = null); - public virtual ClientResult GenerateEmbeddings(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); + public virtual ClientResult GenerateEmbeddings(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); public virtual ClientResult GenerateEmbeddings(IEnumerable inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GenerateEmbeddingsAsync(BinaryContent content, RequestOptions options = null); - public virtual Task> GenerateEmbeddingsAsync(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); + public virtual Task> GenerateEmbeddingsAsync(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); public virtual Task> GenerateEmbeddingsAsync(IEnumerable inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default); } public class EmbeddingGenerationOptions : IJsonModel, IPersistableModel { @@ -1814,7 +1767,6 @@ public class FileClient { public FileClient(ApiKeyCredential credential, OpenAIClientOptions options); public FileClient(ApiKeyCredential credential); protected internal FileClient(ClientPipeline pipeline, OpenAIClientOptions options); - public FileClient(string apiKey, OpenAIClientOptions options); public FileClient(string apiKey); public ClientPipeline Pipeline { get; } public virtual ClientResult AddUploadPart(string uploadId, BinaryContent content, string contentType, RequestOptions options = null); @@ -1843,11 +1795,11 @@ public class FileClient { [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetFileAsync(string fileId, RequestOptions options); public virtual Task> GetFileAsync(string fileId, CancellationToken cancellationToken = default); - public virtual ClientResult GetFiles(OpenAIFilePurpose purpose, CancellationToken cancellationToken = default); + public virtual ClientResult GetFiles(FilePurpose purpose, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GetFiles(string purpose, RequestOptions options); public virtual ClientResult GetFiles(CancellationToken cancellationToken = default); - public virtual Task> GetFilesAsync(OpenAIFilePurpose purpose, CancellationToken cancellationToken = default); + public virtual Task> GetFilesAsync(FilePurpose purpose, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetFilesAsync(string purpose, RequestOptions options); public virtual Task> GetFilesAsync(CancellationToken cancellationToken = default); @@ -1871,6 +1823,21 @@ public class FileDeletionResult : IJsonModel, IPersistableMo string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } + public enum FilePurpose { + Assistants = 0, + AssistantsOutput = 1, + Batch = 2, + BatchOutput = 3, + FineTune = 4, + FineTuneResults = 5, + Vision = 6 + } + [Obsolete("This struct is obsolete. If this is a fine-tuning training file, it may take some time to process after it has been uploaded. While the file is processing, you can still create a fine-tuning job but it will not start until the file processing has completed.")] + public enum FileStatus { + Uploaded = 0, + Processed = 1, + Error = 2 + } public readonly partial struct FileUploadPurpose : IEquatable { private readonly object _dummy; private readonly int _dummyPrimitive; @@ -1893,10 +1860,10 @@ public class OpenAIFile : IJsonModel, IPersistableModel public DateTimeOffset CreatedAt { get; } public string Filename { get; } public string Id { get; } - public OpenAIFilePurpose Purpose { get; } + public FilePurpose Purpose { get; } public int? SizeInBytes { get; } [Obsolete("This property is obsolete. If this is a fine-tuning training file, it may take some time to process after it has been uploaded. While the file is processing, you can still create a fine-tuning job but it will not start until the file processing has completed.")] - public OpenAIFileStatus Status { get; } + public FileStatus Status { get; } [Obsolete("This property is obsolete. For details on why a fine-tuning training file failed validation, see the `error` field on the fine-tuning job.")] public string StatusDetails { get; } OpenAIFile IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options); @@ -1912,49 +1879,10 @@ public class OpenAIFileCollection : ObjectModel.ReadOnlyCollection, string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public readonly partial struct OpenAIFilePurpose : IEquatable { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public OpenAIFilePurpose(string value); - public static OpenAIFilePurpose Assistants { get; } - public static OpenAIFilePurpose AssistantsOutput { get; } - public static OpenAIFilePurpose Batch { get; } - public static OpenAIFilePurpose BatchOutput { get; } - public static OpenAIFilePurpose FineTune { get; } - public static OpenAIFilePurpose FineTuneResults { get; } - public static OpenAIFilePurpose Vision { get; } - public readonly bool Equals(OpenAIFilePurpose other); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly bool Equals(object obj); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly int GetHashCode(); - public static bool operator ==(OpenAIFilePurpose left, OpenAIFilePurpose right); - public static implicit operator OpenAIFilePurpose(string value); - public static bool operator !=(OpenAIFilePurpose left, OpenAIFilePurpose right); - public override readonly string ToString(); - } public static class OpenAIFilesModelFactory { public static FileDeletionResult FileDeletionResult(string fileId = null, bool deleted = false); public static OpenAIFileCollection OpenAIFileCollection(IEnumerable items = null); - public static OpenAIFile OpenAIFileInfo(string id = null, int? sizeInBytes = null, DateTimeOffset createdAt = default, string filename = null, OpenAIFilePurpose purpose = default, OpenAIFileStatus status = default, string statusDetails = null); - } - [Obsolete("This struct is obsolete. If this is a fine-tuning training file, it may take some time to process after it has been uploaded. While the file is processing, you can still create a fine-tuning job but it will not start until the file processing has completed.")] - public readonly partial struct OpenAIFileStatus : IEquatable { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public OpenAIFileStatus(string value); - public static OpenAIFileStatus Error { get; } - public static OpenAIFileStatus Processed { get; } - public static OpenAIFileStatus Uploaded { get; } - public readonly bool Equals(OpenAIFileStatus other); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly bool Equals(object obj); - [EditorBrowsable(EditorBrowsableState.Never)] - public override readonly int GetHashCode(); - public static bool operator ==(OpenAIFileStatus left, OpenAIFileStatus right); - public static implicit operator OpenAIFileStatus(string value); - public static bool operator !=(OpenAIFileStatus left, OpenAIFileStatus right); - public override readonly string ToString(); + public static OpenAIFile OpenAIFileInfo(string id = null, int? sizeInBytes = null, DateTimeOffset createdAt = default, string filename = null, FilePurpose purpose = FilePurpose.Assistants, FileStatus status = FileStatus.Uploaded, string statusDetails = null); } } namespace OpenAI.FineTuning { @@ -1963,22 +1891,33 @@ public class FineTuningClient { public FineTuningClient(ApiKeyCredential credential, OpenAIClientOptions options); public FineTuningClient(ApiKeyCredential credential); protected internal FineTuningClient(ClientPipeline pipeline, OpenAIClientOptions options); - public FineTuningClient(string apiKey, OpenAIClientOptions options); public FineTuningClient(string apiKey); public ClientPipeline Pipeline { get; } - public virtual ClientResult CancelJob(string jobId, RequestOptions options); - public virtual Task CancelJobAsync(string jobId, RequestOptions options); - public virtual ClientResult CreateJob(BinaryContent content, RequestOptions options = null); - public virtual Task CreateJobAsync(BinaryContent content, RequestOptions options = null); - public virtual ClientResult GetJob(string jobId, RequestOptions options); - public virtual Task GetJobAsync(string jobId, RequestOptions options); - public virtual CollectionResult GetJobCheckpoints(string jobId, string after, int? limit, RequestOptions options); - public virtual AsyncCollectionResult GetJobCheckpointsAsync(string jobId, string after, int? limit, RequestOptions options); - public virtual CollectionResult GetJobEvents(string jobId, string after, int? limit, RequestOptions options); - public virtual AsyncCollectionResult GetJobEventsAsync(string jobId, string after, int? limit, RequestOptions options); + public virtual FineTuningJobOperation CreateFineTuningJob(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual Task CreateFineTuningJobAsync(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual ClientResult GetJob(string fineTuningJobId, RequestOptions options); + public virtual Task GetJobAsync(string fineTuningJobId, RequestOptions options); public virtual CollectionResult GetJobs(string after, int? limit, RequestOptions options); public virtual AsyncCollectionResult GetJobsAsync(string after, int? limit, RequestOptions options); } + public class FineTuningJobOperation : OperationResult { + public string JobId { get; } + public override ContinuationToken? RehydrationToken { get; protected set; } + public virtual ClientResult Cancel(RequestOptions? options); + public virtual Task CancelAsync(RequestOptions? options); + public virtual ClientResult GetJob(RequestOptions? options); + public virtual Task GetJobAsync(RequestOptions? options); + public virtual CollectionResult GetJobCheckpoints(string? after, int? limit, RequestOptions? options); + public virtual AsyncCollectionResult GetJobCheckpointsAsync(string? after, int? limit, RequestOptions? options); + public virtual CollectionResult GetJobEvents(string? after, int? limit, RequestOptions options); + public virtual AsyncCollectionResult GetJobEventsAsync(string? after, int? limit, RequestOptions options); + public static FineTuningJobOperation Rehydrate(FineTuningClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static FineTuningJobOperation Rehydrate(FineTuningClient client, string fineTuningJobId, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(FineTuningClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(FineTuningClient client, string fineTuningJobId, CancellationToken cancellationToken = default); + public override ClientResult UpdateStatus(RequestOptions? options = null); + public override ValueTask UpdateStatusAsync(RequestOptions? options = null); + } } namespace OpenAI.Images { public class GeneratedImage : IJsonModel, IPersistableModel { @@ -1999,13 +1938,37 @@ public class GeneratedImageCollection : ObjectModel.ReadOnlyCollection.GetFormatFromOptions(ModelReaderWriterOptions options); BinaryData IPersistableModel.Write(ModelReaderWriterOptions options); } - public enum GeneratedImageFormat { - Bytes = 0, - Uri = 1 + public readonly partial struct GeneratedImageFormat : IEquatable { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public GeneratedImageFormat(string value); + public static GeneratedImageFormat Bytes { get; } + public static GeneratedImageFormat Uri { get; } + public readonly bool Equals(GeneratedImageFormat other); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode(); + public static bool operator ==(GeneratedImageFormat left, GeneratedImageFormat right); + public static implicit operator GeneratedImageFormat(string value); + public static bool operator !=(GeneratedImageFormat left, GeneratedImageFormat right); + public override readonly string ToString(); } - public enum GeneratedImageQuality { - High = 0, - Standard = 1 + public readonly partial struct GeneratedImageQuality : IEquatable { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public GeneratedImageQuality(string value); + public static GeneratedImageQuality High { get; } + public static GeneratedImageQuality Standard { get; } + public readonly bool Equals(GeneratedImageQuality other); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode(); + public static bool operator ==(GeneratedImageQuality left, GeneratedImageQuality right); + public static implicit operator GeneratedImageQuality(string value); + public static bool operator !=(GeneratedImageQuality left, GeneratedImageQuality right); + public override readonly string ToString(); } public readonly partial struct GeneratedImageSize : IEquatable { private readonly object _dummy; @@ -2025,16 +1988,27 @@ public enum GeneratedImageQuality { public static bool operator !=(GeneratedImageSize left, GeneratedImageSize right); public override readonly string ToString(); } - public enum GeneratedImageStyle { - Vivid = 0, - Natural = 1 + public readonly partial struct GeneratedImageStyle : IEquatable { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public GeneratedImageStyle(string value); + public static GeneratedImageStyle Natural { get; } + public static GeneratedImageStyle Vivid { get; } + public readonly bool Equals(GeneratedImageStyle other); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object obj); + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode(); + public static bool operator ==(GeneratedImageStyle left, GeneratedImageStyle right); + public static implicit operator GeneratedImageStyle(string value); + public static bool operator !=(GeneratedImageStyle left, GeneratedImageStyle right); + public override readonly string ToString(); } public class ImageClient { protected ImageClient(); protected internal ImageClient(ClientPipeline pipeline, string model, OpenAIClientOptions options); public ImageClient(string model, ApiKeyCredential credential, OpenAIClientOptions options); public ImageClient(string model, ApiKeyCredential credential); - public ImageClient(string model, string apiKey, OpenAIClientOptions options); public ImageClient(string model, string apiKey); public ClientPipeline Pipeline { get; } public virtual ClientResult GenerateImage(string prompt, ImageGenerationOptions options = null, CancellationToken cancellationToken = default); @@ -2121,7 +2095,6 @@ public class ModelClient { public ModelClient(ApiKeyCredential credential, OpenAIClientOptions options); public ModelClient(ApiKeyCredential credential); protected internal ModelClient(ClientPipeline pipeline, OpenAIClientOptions options); - public ModelClient(string apiKey, OpenAIClientOptions options); public ModelClient(string apiKey); public ClientPipeline Pipeline { get; } [EditorBrowsable(EditorBrowsableState.Never)] @@ -2185,7 +2158,6 @@ public class ModerationClient { protected internal ModerationClient(ClientPipeline pipeline, string model, OpenAIClientOptions options); public ModerationClient(string model, ApiKeyCredential credential, OpenAIClientOptions options); public ModerationClient(string model, ApiKeyCredential credential); - public ModerationClient(string model, string apiKey, OpenAIClientOptions options); public ModerationClient(string model, string apiKey); public ClientPipeline Pipeline { get; } [EditorBrowsable(EditorBrowsableState.Never)] @@ -2232,6 +2204,62 @@ public static class OpenAIModerationsModelFactory { } } namespace OpenAI.VectorStores { + public class AddFileToVectorStoreOperation : OperationResult { + public string FileId { get; } + public override ContinuationToken? RehydrationToken { get; protected set; } + public VectorStoreFileAssociationStatus? Status { get; } + public VectorStoreFileAssociation? Value { get; } + public string VectorStoreId { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetFileAssociation(RequestOptions? options); + public virtual ClientResult GetFileAssociation(CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual Task GetFileAssociationAsync(RequestOptions? options); + public virtual Task> GetFileAssociationAsync(CancellationToken cancellationToken = default); + public static AddFileToVectorStoreOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public override ClientResult UpdateStatus(RequestOptions? options = null); + public override ValueTask UpdateStatusAsync(RequestOptions? options = null); + } + public class CreateBatchFileJobOperation : OperationResult { + public string BatchId { get; } + public override ContinuationToken? RehydrationToken { get; protected set; } + public VectorStoreBatchFileJobStatus? Status { get; } + public VectorStoreBatchFileJob? Value { get; } + public string VectorStoreId { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult Cancel(RequestOptions? options); + public virtual ClientResult Cancel(CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual Task CancelAsync(RequestOptions? options); + public virtual Task> CancelAsync(CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetFileBatch(RequestOptions? options); + public virtual ClientResult GetFileBatch(CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual Task GetFileBatchAsync(RequestOptions? options); + public virtual Task> GetFileBatchAsync(CancellationToken cancellationToken = default); + public static CreateBatchFileJobOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public override ClientResult UpdateStatus(RequestOptions? options = null); + public override ValueTask UpdateStatusAsync(RequestOptions? options = null); + } + public class CreateVectorStoreOperation : OperationResult { + public override ContinuationToken? RehydrationToken { get; protected set; } + public VectorStoreStatus? Status { get; } + public VectorStore? Value { get; } + public string VectorStoreId { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetVectorStore(RequestOptions? options); + public virtual ClientResult GetVectorStore(CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual Task GetVectorStoreAsync(RequestOptions? options); + public virtual Task> GetVectorStoreAsync(CancellationToken cancellationToken = default); + public static CreateVectorStoreOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public static Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default); + public override ClientResult UpdateStatus(RequestOptions? options = null); + public override ValueTask UpdateStatusAsync(RequestOptions? options = null); + } public abstract class FileChunkingStrategy : IJsonModel, IPersistableModel { public static FileChunkingStrategy Auto { get; } public static FileChunkingStrategy Unknown { get; } @@ -2313,64 +2341,48 @@ public class VectorStoreClient { public VectorStoreClient(ApiKeyCredential credential, OpenAIClientOptions options); public VectorStoreClient(ApiKeyCredential credential); protected internal VectorStoreClient(ClientPipeline pipeline, OpenAIClientOptions options); - public VectorStoreClient(string apiKey, OpenAIClientOptions options); public VectorStoreClient(string apiKey); public ClientPipeline Pipeline { get; } - public virtual ClientResult AddFileToVectorStore(VectorStore vectorStore, OpenAIFile file); [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult AddFileToVectorStore(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult AddFileToVectorStore(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); - public virtual Task> AddFileToVectorStoreAsync(VectorStore vectorStore, OpenAIFile file); + public virtual AddFileToVectorStoreOperation AddFileToVectorStore(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual AddFileToVectorStoreOperation AddFileToVectorStore(string vectorStoreId, string fileId, bool waitUntilCompleted, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Task AddFileToVectorStoreAsync(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual Task> AddFileToVectorStoreAsync(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); - public virtual ClientResult CancelBatchFileJob(VectorStoreBatchFileJob batchJob); + public virtual Task AddFileToVectorStoreAsync(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual Task AddFileToVectorStoreAsync(string vectorStoreId, string fileId, bool waitUntilCompleted, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchId, RequestOptions options); public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default); - public virtual Task> CancelBatchFileJobAsync(VectorStoreBatchFileJob batchJob); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task CancelBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options); public virtual Task> CancelBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default); - public virtual ClientResult CreateBatchFileJob(VectorStore vectorStore, IEnumerable files); [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult CreateBatchFileJob(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult CreateBatchFileJob(string vectorStoreId, IEnumerable fileIds, CancellationToken cancellationToken = default); - public virtual Task> CreateBatchFileJobAsync(VectorStore vectorStore, IEnumerable files); + public virtual CreateBatchFileJobOperation CreateBatchFileJob(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual CreateBatchFileJobOperation CreateBatchFileJob(string vectorStoreId, IEnumerable fileIds, bool waitUntilCompleted, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Task CreateBatchFileJobAsync(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual Task> CreateBatchFileJobAsync(string vectorStoreId, IEnumerable fileIds, CancellationToken cancellationToken = default); - public virtual ClientResult CreateVectorStore(VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default); + public virtual Task CreateBatchFileJobAsync(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); + public virtual Task CreateBatchFileJobAsync(string vectorStoreId, IEnumerable fileIds, bool waitUntilCompleted, CancellationToken cancellationToken = default); + public virtual CreateVectorStoreOperation CreateVectorStore(bool waitUntilCompleted, VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] + public virtual CreateVectorStoreOperation CreateVectorStore(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); public virtual ClientResult CreateVectorStore(BinaryContent content, RequestOptions options = null); - public virtual Task> CreateVectorStoreAsync(VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default); + public virtual Task CreateVectorStoreAsync(bool waitUntilCompleted, VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default); + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual Task CreateVectorStoreAsync(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null); public virtual Task CreateVectorStoreAsync(BinaryContent content, RequestOptions options = null); - public virtual ClientResult DeleteVectorStore(VectorStore vectorStore); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult DeleteVectorStore(string vectorStoreId, RequestOptions options); public virtual ClientResult DeleteVectorStore(string vectorStoreId, CancellationToken cancellationToken = default); - public virtual Task> DeleteVectorStoreAsync(VectorStore vectorStore); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task DeleteVectorStoreAsync(string vectorStoreId, RequestOptions options); public virtual Task> DeleteVectorStoreAsync(string vectorStoreId, CancellationToken cancellationToken = default); - public virtual ClientResult GetBatchFileJob(VectorStoreBatchFileJob batchJob); - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchId, RequestOptions options); public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default); - public virtual Task> GetBatchFileJobAsync(VectorStoreBatchFileJob batchJob); - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Task GetBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options); public virtual Task> GetBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default); - public virtual ClientResult GetFileAssociation(VectorStore vectorStore, OpenAIFile file); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult GetFileAssociation(string vectorStoreId, string fileId, RequestOptions options); public virtual ClientResult GetFileAssociation(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); - public virtual Task> GetFileAssociationAsync(VectorStore vectorStore, OpenAIFile file); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task GetFileAssociationAsync(string vectorStoreId, string fileId, RequestOptions options); public virtual Task> GetFileAssociationAsync(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); - public virtual CollectionResult GetFileAssociations(VectorStore vectorStore, VectorStoreFileAssociationCollectionOptions options = null); - public virtual CollectionResult GetFileAssociations(VectorStoreBatchFileJob batchJob, VectorStoreFileAssociationCollectionOptions options = null); public virtual CollectionResult GetFileAssociations(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual CollectionResult GetFileAssociations(string vectorStoreId, VectorStoreFileAssociationCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] @@ -2379,8 +2391,6 @@ public class VectorStoreClient { public virtual CollectionResult GetFileAssociations(string vectorStoreId, string batchJobId, ContinuationToken firstPageToken, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual CollectionResult GetFileAssociations(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options); - public virtual AsyncCollectionResult GetFileAssociationsAsync(VectorStore vectorStore, VectorStoreFileAssociationCollectionOptions options = null); - public virtual AsyncCollectionResult GetFileAssociationsAsync(VectorStoreBatchFileJob batchJob, VectorStoreFileAssociationCollectionOptions options = null); public virtual AsyncCollectionResult GetFileAssociationsAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); public virtual AsyncCollectionResult GetFileAssociationsAsync(string vectorStoreId, VectorStoreFileAssociationCollectionOptions options = null, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] @@ -2389,14 +2399,7 @@ public class VectorStoreClient { public virtual AsyncCollectionResult GetFileAssociationsAsync(string vectorStoreId, string batchJobId, ContinuationToken firstPageToken, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual AsyncCollectionResult GetFileAssociationsAsync(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options); - public virtual ClientResult GetVectorStore(VectorStore vectorStore); - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult GetVectorStore(string vectorStoreId, RequestOptions options); public virtual ClientResult GetVectorStore(string vectorStoreId, CancellationToken cancellationToken = default); - public virtual Task> GetVectorStoreAsync(VectorStore vectorStore); - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Task GetVectorStoreAsync(string vectorStoreId, RequestOptions options); - public virtual Task> GetVectorStoreAsync(string vectorStoreId, CancellationToken cancellationToken = default); public virtual CollectionResult GetVectorStores(VectorStoreCollectionOptions options = null, CancellationToken cancellationToken = default); public virtual CollectionResult GetVectorStores(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] @@ -2405,19 +2408,15 @@ public class VectorStoreClient { public virtual AsyncCollectionResult GetVectorStoresAsync(ContinuationToken firstPageToken, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual AsyncCollectionResult GetVectorStoresAsync(int? limit, string order, string after, string before, RequestOptions options); - public virtual ClientResult ModifyVectorStore(VectorStore vectorStore, VectorStoreModificationOptions options); public virtual ClientResult ModifyVectorStore(string vectorStoreId, VectorStoreModificationOptions vectorStore, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult ModifyVectorStore(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual Task> ModifyVectorStoreAsync(VectorStore vectorStore, VectorStoreModificationOptions options); public virtual Task> ModifyVectorStoreAsync(string vectorStoreId, VectorStoreModificationOptions vectorStore, CancellationToken cancellationToken = default); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task ModifyVectorStoreAsync(string vectorStoreId, BinaryContent content, RequestOptions options = null); - public virtual ClientResult RemoveFileFromStore(VectorStore vectorStore, OpenAIFile file); [EditorBrowsable(EditorBrowsableState.Never)] public virtual ClientResult RemoveFileFromStore(string vectorStoreId, string fileId, RequestOptions options); public virtual ClientResult RemoveFileFromStore(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); - public virtual Task> RemoveFileFromStoreAsync(VectorStore vectorStore, OpenAIFile file); [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task RemoveFileFromStoreAsync(string vectorStoreId, string fileId, RequestOptions options); public virtual Task> RemoveFileFromStoreAsync(string vectorStoreId, string fileId, CancellationToken cancellationToken = default); diff --git a/examples/Assets/picture-of-apple.png b/examples/Assets/picture-of-apple.png new file mode 100644 index 00000000..4e04c26b Binary files /dev/null and b/examples/Assets/picture-of-apple.png differ diff --git a/examples/Assets/picture-of-orange.png b/examples/Assets/picture-of-orange.png new file mode 100644 index 00000000..30e8e892 Binary files /dev/null and b/examples/Assets/picture-of-orange.png differ diff --git a/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs b/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs index 148ef0e5..8a900766 100644 --- a/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs +++ b/examples/Assistants/Example01_RetrievalAugmentedGeneration.cs @@ -3,7 +3,6 @@ using OpenAI.Files; using System; using System.ClientModel; -using System.Collections.Generic; using System.IO; using System.Threading; @@ -15,6 +14,7 @@ public partial class AssistantExamples public void Example01_RetrievalAugmentedGeneration() { // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning. + #pragma warning disable OPENAI001 OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY")); FileClient fileClient = openAIClient.GetFileClient(); AssistantClient assistantClient = openAIClient.GetAssistantClient(); @@ -142,7 +142,7 @@ CollectionResult messages // Optionally, delete any persistent resources you no longer need. _ = assistantClient.DeleteThread(threadRun.ThreadId); - _ = assistantClient.DeleteAssistant(assistant); + _ = assistantClient.DeleteAssistant(assistant.Id); _ = fileClient.DeleteFile(salesFile.Id); } } diff --git a/examples/Assistants/Example01_RetrievalAugmentedGenerationAsync.cs b/examples/Assistants/Example01_RetrievalAugmentedGenerationAsync.cs index ec6b3368..431cc5c2 100644 --- a/examples/Assistants/Example01_RetrievalAugmentedGenerationAsync.cs +++ b/examples/Assistants/Example01_RetrievalAugmentedGenerationAsync.cs @@ -3,7 +3,6 @@ using OpenAI.Files; using System; using System.ClientModel; -using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -16,6 +15,7 @@ public partial class AssistantExamples public async Task Example01_RetrievalAugmentedGenerationAsync() { // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning. + #pragma warning disable OPENAI001 OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY")); FileClient fileClient = openAIClient.GetFileClient(); AssistantClient assistantClient = openAIClient.GetAssistantClient(); @@ -143,7 +143,7 @@ AsyncCollectionResult messages // Optionally, delete any persistent resources you no longer need. _ = await assistantClient.DeleteThreadAsync(threadRun.ThreadId); - _ = await assistantClient.DeleteAssistantAsync(assistant); + _ = await assistantClient.DeleteAssistantAsync(assistant.Id); _ = await fileClient.DeleteFileAsync(salesFile.Id); } } diff --git a/examples/Assistants/Example02b_FunctionCallingStreaming.cs b/examples/Assistants/Example02b_FunctionCallingStreaming.cs index d4499015..b5107963 100644 --- a/examples/Assistants/Example02b_FunctionCallingStreaming.cs +++ b/examples/Assistants/Example02b_FunctionCallingStreaming.cs @@ -82,7 +82,7 @@ public async Task Example02b_FunctionCallingStreaming() #region Step 2 - Create a thread and add messages AssistantThread thread = await client.CreateThreadAsync(); ThreadMessage message = await client.CreateMessageAsync( - thread, + thread.Id, MessageRole.User, [ "What's the weather in San Francisco today and the likelihood it'll rain?" @@ -91,7 +91,7 @@ public async Task Example02b_FunctionCallingStreaming() #region Step 3 - Initiate a streaming run AsyncCollectionResult asyncUpdates - = client.CreateRunStreamingAsync(thread, assistant); + = client.CreateRunStreamingAsync(thread.Id, assistant.Id); ThreadRun currentRun = null; do @@ -100,11 +100,7 @@ AsyncCollectionResult asyncUpdates List outputsToSubmit = []; await foreach (StreamingUpdate update in asyncUpdates) { - if (update is RunUpdate runUpdate) - { - currentRun = runUpdate; - } - else if (update is RequiredActionUpdate requiredActionUpdate) + if (update is RequiredActionUpdate requiredActionUpdate) { if (requiredActionUpdate.FunctionName == getTemperatureTool.FunctionName) { @@ -115,6 +111,10 @@ AsyncCollectionResult asyncUpdates outputsToSubmit.Add(new ToolOutput(requiredActionUpdate.ToolCallId, "25%")); } } + else if (update is RunUpdate runUpdate) + { + currentRun = runUpdate; + } else if (update is MessageContentUpdate contentUpdate) { Console.Write(contentUpdate.Text); @@ -122,7 +122,7 @@ AsyncCollectionResult asyncUpdates } if (outputsToSubmit.Count > 0) { - asyncUpdates = client.SubmitToolOutputsToRunStreamingAsync(currentRun, outputsToSubmit); + asyncUpdates = client.SubmitToolOutputsToRunStreamingAsync(currentRun.ThreadId, currentRun.Id, outputsToSubmit); } } while (currentRun?.Status.IsTerminal == false); diff --git a/examples/Assistants/Example04_AllTheTools.cs b/examples/Assistants/Example04_AllTheTools.cs index 62b76a43..8c268c3e 100644 --- a/examples/Assistants/Example04_AllTheTools.cs +++ b/examples/Assistants/Example04_AllTheTools.cs @@ -87,7 +87,7 @@ static string GetNameOfFamilyMember(string relation) } }); - ThreadRun run = client.CreateRun(thread, assistant); + ThreadRun run = client.CreateRun(thread.Id, assistant.Id); #endregion #region Complete the run, calling functions as needed @@ -169,7 +169,9 @@ CollectionResult messages #region List run steps for details about tool calls CollectionResult runSteps = client.GetRunSteps( - run, new RunStepCollectionOptions() + run.ThreadId, + run.Id, + new RunStepCollectionOptions() { Order = RunStepCollectionOrder.Ascending }); @@ -194,8 +196,8 @@ CollectionResult messages #endregion #region Clean up any temporary resources that are no longer needed - _ = client.DeleteThread(thread); - _ = client.DeleteAssistant(assistant); + _ = client.DeleteThread(thread.Id); + _ = client.DeleteAssistant(assistant.Id); _ = fileClient.DeleteFile(favoriteNumberFile.Id); #endregion } diff --git a/examples/Assistants/Example05_AssistantsWithVision.cs b/examples/Assistants/Example05_AssistantsWithVision.cs index 94460187..9114405c 100644 --- a/examples/Assistants/Example05_AssistantsWithVision.cs +++ b/examples/Assistants/Example05_AssistantsWithVision.cs @@ -3,6 +3,7 @@ using OpenAI.Files; using System; using System.ClientModel; +using System.IO; namespace OpenAI.Examples; @@ -12,12 +13,13 @@ public partial class AssistantExamples public void Example05_AssistantsWithVision() { // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning. + #pragma warning disable OPENAI001 OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY")); FileClient fileClient = openAIClient.GetFileClient(); AssistantClient assistantClient = openAIClient.GetAssistantClient(); OpenAIFile pictureOfAppleFile = fileClient.UploadFile( - "picture-of-apple.jpg", + Path.Combine("Assets", "picture-of-apple.png"), FileUploadPurpose.Vision); Uri linkToPictureOfOrange = new("https://platform.openai.com/fictitious-files/picture-of-orange.png"); @@ -44,8 +46,8 @@ public void Example05_AssistantsWithVision() }); CollectionResult streamingUpdates = assistantClient.CreateRunStreaming( - thread, - assistant, + thread.Id, + assistant.Id, new RunCreationOptions() { AdditionalInstructions = "When possible, try to sneak in puns if you're asked to compare things.", @@ -65,7 +67,7 @@ public void Example05_AssistantsWithVision() // Delete temporary resources, if desired _ = fileClient.DeleteFile(pictureOfAppleFile.Id); - _ = assistantClient.DeleteThread(thread); - _ = assistantClient.DeleteAssistant(assistant); + _ = assistantClient.DeleteThread(thread.Id); + _ = assistantClient.DeleteAssistant(assistant.Id); } } diff --git a/examples/Assistants/Example05_AssistantsWithVisionAsync.cs b/examples/Assistants/Example05_AssistantsWithVisionAsync.cs index 97443aad..88ed4237 100644 --- a/examples/Assistants/Example05_AssistantsWithVisionAsync.cs +++ b/examples/Assistants/Example05_AssistantsWithVisionAsync.cs @@ -3,6 +3,7 @@ using OpenAI.Files; using System; using System.ClientModel; +using System.IO; using System.Threading.Tasks; namespace OpenAI.Examples; @@ -13,12 +14,13 @@ public partial class AssistantExamples public async Task Example05_AssistantsWithVisionAsync() { // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning. + #pragma warning disable OPENAI001 OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY")); FileClient fileClient = openAIClient.GetFileClient(); AssistantClient assistantClient = openAIClient.GetAssistantClient(); OpenAIFile pictureOfAppleFile = await fileClient.UploadFileAsync( - "picture-of-apple.jpg", + Path.Combine("Assets", "picture-of-apple.png"), FileUploadPurpose.Vision); Uri linkToPictureOfOrange = new("https://platform.openai.com/fictitious-files/picture-of-orange.png"); @@ -45,8 +47,8 @@ public async Task Example05_AssistantsWithVisionAsync() }); AsyncCollectionResult streamingUpdates = assistantClient.CreateRunStreamingAsync( - thread, - assistant, + thread.Id, + assistant.Id, new RunCreationOptions() { AdditionalInstructions = "When possible, try to sneak in puns if you're asked to compare things.", @@ -65,7 +67,7 @@ public async Task Example05_AssistantsWithVisionAsync() } _ = await fileClient.DeleteFileAsync(pictureOfAppleFile.Id); - _ = await assistantClient.DeleteThreadAsync(thread); - _ = await assistantClient.DeleteAssistantAsync(assistant); + _ = await assistantClient.DeleteThreadAsync(thread.Id); + _ = await assistantClient.DeleteAssistantAsync(assistant.Id); } } diff --git a/examples/Audio/Example03_VerboseTranscription.cs b/examples/Audio/Example03_VerboseTranscription.cs index a020c34b..daef9933 100644 --- a/examples/Audio/Example03_VerboseTranscription.cs +++ b/examples/Audio/Example03_VerboseTranscription.cs @@ -17,7 +17,7 @@ public void Example03_VerboseTranscription() AudioTranscriptionOptions options = new() { ResponseFormat = AudioTranscriptionFormat.Verbose, - Granularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, + TimestampGranularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, }; AudioTranscription transcription = client.TranscribeAudio(audioFilePath, options); diff --git a/examples/Audio/Example03_VerboseTrascriptionAsync.cs b/examples/Audio/Example03_VerboseTrascriptionAsync.cs index 4f25f300..07b916c1 100644 --- a/examples/Audio/Example03_VerboseTrascriptionAsync.cs +++ b/examples/Audio/Example03_VerboseTrascriptionAsync.cs @@ -18,7 +18,7 @@ public async Task Example03_VerboseTranscriptionAsync() AudioTranscriptionOptions options = new() { ResponseFormat = AudioTranscriptionFormat.Verbose, - Granularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, + TimestampGranularities = AudioTimestampGranularities.Word | AudioTimestampGranularities.Segment, }; AudioTranscription transcription = await client.TranscribeAudioAsync(audioFilePath, options); diff --git a/examples/Chat/Example01_SimpleChatAsync.cs b/examples/Chat/Example01_SimpleChatAsync.cs index 4325eae1..1b105a25 100644 --- a/examples/Chat/Example01_SimpleChatAsync.cs +++ b/examples/Chat/Example01_SimpleChatAsync.cs @@ -14,6 +14,6 @@ public async Task Example01_SimpleChatAsync() ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'"); - Console.WriteLine($"{completion}"); + Console.WriteLine($"[ASSISTANT]: {completion}"); } } diff --git a/examples/Chat/Example02_SimpleChatStreaming.cs b/examples/Chat/Example02_SimpleChatStreaming.cs index 17930436..30f2238c 100644 --- a/examples/Chat/Example02_SimpleChatStreaming.cs +++ b/examples/Chat/Example02_SimpleChatStreaming.cs @@ -20,7 +20,7 @@ CollectionResult updates { foreach (ChatMessageContentPart updatePart in update.ContentUpdate) { - Console.Write(updatePart); + Console.Write(updatePart.Text); } } } diff --git a/examples/Chat/Example06_StructuredOutputs.cs b/examples/Chat/Example06_StructuredOutputs.cs index bb1c76df..3b8fe170 100644 --- a/examples/Chat/Example06_StructuredOutputs.cs +++ b/examples/Chat/Example06_StructuredOutputs.cs @@ -13,10 +13,6 @@ public void Example06_StructuredOutputs() { ChatClient client = new("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); - List messages = [ - new UserChatMessage("How can I solve 8x + 7 = -23?"), - ]; - ChatCompletionOptions options = new() { ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat( @@ -46,7 +42,9 @@ public void Example06_StructuredOutputs() jsonSchemaIsStrict: true) }; - ChatCompletion chatCompletion = client.CompleteChat(messages, options); + ChatCompletion chatCompletion = client.CompleteChat( + ["How can I solve 8x + 7 = -23?"], + options); using JsonDocument structuredJson = JsonDocument.Parse(chatCompletion.ToString()); diff --git a/examples/Chat/Example06_StructuredOutputsAsync.cs b/examples/Chat/Example06_StructuredOutputsAsync.cs index dcc4fffe..e9c212b9 100644 --- a/examples/Chat/Example06_StructuredOutputsAsync.cs +++ b/examples/Chat/Example06_StructuredOutputsAsync.cs @@ -14,10 +14,6 @@ public async Task Example06_StructuredOutputsAsync() { ChatClient client = new("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); - List messages = [ - new UserChatMessage("How can I solve 8x + 7 = -23?"), - ]; - ChatCompletionOptions options = new() { ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat( @@ -47,7 +43,9 @@ public async Task Example06_StructuredOutputsAsync() jsonSchemaIsStrict: true) }; - ChatCompletion chatCompletion = await client.CompleteChatAsync(messages, options); + ChatCompletion chatCompletion = await client.CompleteChatAsync( + ["How can I solve 8x + 7 = -23?"], + options); using JsonDocument structuredJson = JsonDocument.Parse(chatCompletion.ToString()); diff --git a/examples/MockClientExamples.cs b/examples/MockClientExamples.cs new file mode 100644 index 00000000..9b6bfd0b --- /dev/null +++ b/examples/MockClientExamples.cs @@ -0,0 +1,44 @@ +using System.ClientModel.Primitives; +using System.ClientModel; +using Moq; +using NUnit.Framework; +using OpenAI.Audio; + +namespace OpenAI.Examples.Miscellaneous; + +public partial class MockClientExamples +{ + [Test] + public void MockClient() + { + // Instantiate mocks and the AudioTranscription object. + + Mock mockClient = new(); + Mock> mockResult = new(null, Mock.Of()); + AudioTranscription transcription = OpenAIAudioModelFactory.AudioTranscription(text: "I swear I saw an apple flying yesterday!"); + + // Set up mocks' properties and methods. + + mockResult + .SetupGet(result => result.Value) + .Returns(transcription); + + mockClient.Setup(client => client.TranscribeAudio( + It.IsAny(), + It.IsAny())) + .Returns(mockResult.Object); + + // Perform validation. + + AudioClient client = mockClient.Object; + bool containsSecretWord = ContainsSecretWord(client, "", "apple"); + + Assert.That(containsSecretWord, Is.True); + } + + public bool ContainsSecretWord(AudioClient client, string audioFilePath, string secretWord) + { + AudioTranscription transcription = client.TranscribeAudio(audioFilePath); + return transcription.Text.Contains(secretWord); + } +} diff --git a/src/Custom/Assistants/AssistantClient.Convenience.cs b/src/Custom/Assistants/AssistantClient.Convenience.cs deleted file mode 100644 index cf72cfff..00000000 --- a/src/Custom/Assistants/AssistantClient.Convenience.cs +++ /dev/null @@ -1,446 +0,0 @@ -using System.ClientModel; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace OpenAI.Assistants; - -public partial class AssistantClient -{ - /// - /// Modifies an existing . - /// - /// The assistant to modify. - /// The changes to apply to the assistant. - /// - /// An updated instance that reflects the requested changes. - /// - public virtual Task> ModifyAssistantAsync(Assistant assistant, AssistantModificationOptions options) - => ModifyAssistantAsync(assistant?.Id, options); - - /// - /// Modifies an existing . - /// - /// The assistant to modify. - /// The changes to apply to the assistant. - /// - /// An updated instance that reflects the requested changes. - /// - public virtual ClientResult ModifyAssistant(Assistant assistant, AssistantModificationOptions options) - => ModifyAssistant(assistant?.Id, options); - - /// - /// Deletes an existing . - /// - /// The assistant to delete. - /// A value indicating whether the deletion was successful. - public virtual Task> DeleteAssistantAsync(Assistant assistant) - => DeleteAssistantAsync(assistant?.Id); - - /// - /// Deletes an existing . - /// - /// The assistant to delete. - /// A value indicating whether the deletion was successful. - public virtual ClientResult DeleteAssistant(Assistant assistant) - => DeleteAssistant(assistant?.Id); - - /// - /// Gets an updated instance of an existing . - /// - /// The existing thread to refresh the state of. - /// An updated instance of the provided . - public virtual Task> GetThreadAsync(AssistantThread thread) - => GetThreadAsync(thread?.Id); - - /// - /// Gets an updated instance of an existing . - /// - /// The existing thread to refresh the state of. - /// An updated instance of the provided . - public virtual ClientResult GetThread(AssistantThread thread) - => GetThread(thread?.Id); - - /// - /// Modifies an existing . - /// - /// The thread to modify. - /// The modifications to apply to the thread. - /// The updated instance. - public virtual Task> ModifyThreadAsync(AssistantThread thread, ThreadModificationOptions options) - => ModifyThreadAsync(thread?.Id, options); - - /// - /// Modifies an existing . - /// - /// The thread to modify. - /// The modifications to apply to the thread. - /// The updated instance. - public virtual ClientResult ModifyThread(AssistantThread thread, ThreadModificationOptions options) - => ModifyThread(thread?.Id, options); - - /// - /// Deletes an existing . - /// - /// The thread to delete. - /// A value indicating whether the deletion was successful. - public virtual Task> DeleteThreadAsync(AssistantThread thread) - => DeleteThreadAsync(thread?.Id); - - /// - /// Deletes an existing . - /// - /// The thread to delete. - /// A value indicating whether the deletion was successful. - public virtual ClientResult DeleteThread(AssistantThread thread) - => DeleteThread(thread?.Id); - - /// - /// Creates a new on an existing . - /// - /// The thread to associate the new message with. - /// The role to associate with the new message. - /// The collection of items for the message. - /// Additional options to apply to the new message. - /// A new . - public virtual Task> CreateMessageAsync( - AssistantThread thread, - MessageRole role, - IEnumerable content, - MessageCreationOptions options = null) - => CreateMessageAsync(thread?.Id, role, content, options); - - /// - /// Creates a new on an existing . - /// - /// The thread to associate the new message with. - /// The role to associate with the new message. - /// The collection of items for the message. - /// Additional options to apply to the new message. - /// A new . - public virtual ClientResult CreateMessage( - AssistantThread thread, - MessageRole role, - IEnumerable content, - MessageCreationOptions options = null) - => CreateMessage(thread?.Id, role, content, options); - - /// - /// Gets a page collection holding instances from an existing . - /// - /// The thread to list messages from. - /// Options describing the collection to return. - /// A collection of . - public virtual AsyncCollectionResult GetMessagesAsync( - AssistantThread thread, - MessageCollectionOptions options = default) - { - Argument.AssertNotNull(thread, nameof(thread)); - - return GetMessagesAsync(thread.Id, options); - } - - /// - /// Gets a page collection holding instances from an existing . - /// - /// The thread to list messages from. - /// Options describing the collection to return. - /// A collection of . - public virtual CollectionResult GetMessages( - AssistantThread thread, - MessageCollectionOptions options = default) - { - Argument.AssertNotNull(thread, nameof(thread)); - - return GetMessages(thread.Id, options); - } - - /// - /// Gets an updated instance of an existing . - /// - /// The existing message to refresh the state of. - /// An updated instance of the provided . - public virtual Task> GetMessageAsync(ThreadMessage message) - => GetMessageAsync(message?.ThreadId, message?.Id); - - /// - /// Gets an updated instance of an existing . - /// - /// The existing message to refresh the state of. - /// An updated instance of the provided . - public virtual ClientResult GetMessage(ThreadMessage message) - => GetMessage(message?.ThreadId, message?.Id); - - /// - /// Modifies an existing . - /// - /// The message to modify. - /// The changes to apply to the message. - /// The updated . - public virtual Task> ModifyMessageAsync(ThreadMessage message, MessageModificationOptions options) - => ModifyMessageAsync(message?.ThreadId, message?.Id, options); - - /// - /// Modifies an existing . - /// - /// The message to modify. - /// The changes to apply to the message. - /// The updated . - public virtual ClientResult ModifyMessage(ThreadMessage message, MessageModificationOptions options) - => ModifyMessage(message?.ThreadId, message?.Id, options); - - /// - /// Deletes an existing . - /// - /// The message to delete. - /// A value indicating whether the deletion was successful. - public virtual Task> DeleteMessageAsync(ThreadMessage message) - => DeleteMessageAsync(message?.ThreadId, message?.Id); - - /// - /// Deletes an existing . - /// - /// The message to delete. - /// A value indicating whether the deletion was successful. - public virtual ClientResult DeleteMessage(ThreadMessage message) - => DeleteMessage(message?.ThreadId, message?.Id); - - /// - /// Begins a new that evaluates a using a specified - /// . - /// - /// The thread that the run should evaluate. - /// The assistant that should be used when evaluating the thread. - /// Additional options for the run. - /// A new instance. - public virtual Task> CreateRunAsync(AssistantThread thread, Assistant assistant, RunCreationOptions options = null) - => CreateRunAsync(thread?.Id, assistant?.Id, options); - - /// - /// Begins a new that evaluates a using a specified - /// . - /// - /// The thread that the run should evaluate. - /// The assistant that should be used when evaluating the thread. - /// Additional options for the run. - /// A new instance. - public virtual ClientResult CreateRun(AssistantThread thread, Assistant assistant, RunCreationOptions options = null) - => CreateRun(thread?.Id, assistant?.Id, options); - - /// - /// Begins a new streaming that evaluates a using a specified - /// . - /// - /// The thread that the run should evaluate. - /// The assistant that should be used when evaluating the thread. - /// Additional options for the run. - public virtual AsyncCollectionResult CreateRunStreamingAsync( - AssistantThread thread, - Assistant assistant, - RunCreationOptions options = null) - => CreateRunStreamingAsync(thread?.Id, assistant?.Id, options); - - /// - /// Begins a new streaming that evaluates a using a specified - /// . - /// - /// The thread that the run should evaluate. - /// The assistant that should be used when evaluating the thread. - /// Additional options for the run. - public virtual CollectionResult CreateRunStreaming( - AssistantThread thread, - Assistant assistant, - RunCreationOptions options = null) - => CreateRunStreaming(thread?.Id, assistant?.Id, options); - - /// - /// Creates a new thread and immediately begins a run against it using the specified . - /// - /// The assistant that the new run should use. - /// Options for the new thread that will be created. - /// Additional options to apply to the run that will begin. - /// A new . - public virtual Task> CreateThreadAndRunAsync( - Assistant assistant, - ThreadCreationOptions threadOptions = null, - RunCreationOptions runOptions = null) - => CreateThreadAndRunAsync(assistant?.Id, threadOptions, runOptions); - - /// - /// Creates a new thread and immediately begins a run against it using the specified . - /// - /// The assistant that the new run should use. - /// Options for the new thread that will be created. - /// Additional options to apply to the run that will begin. - /// A new . - public virtual ClientResult CreateThreadAndRun( - Assistant assistant, - ThreadCreationOptions threadOptions = null, - RunCreationOptions runOptions = null) - => CreateThreadAndRun(assistant?.Id, threadOptions, runOptions); - - /// - /// Creates a new thread and immediately begins a streaming run against it using the specified . - /// - /// The assistant that the new run should use. - /// Options for the new thread that will be created. - /// Additional options to apply to the run that will begin. - public virtual AsyncCollectionResult CreateThreadAndRunStreamingAsync( - Assistant assistant, - ThreadCreationOptions threadOptions = null, - RunCreationOptions runOptions = null) - => CreateThreadAndRunStreamingAsync(assistant?.Id, threadOptions, runOptions); - - /// - /// Creates a new thread and immediately begins a streaming run against it using the specified . - /// - /// The assistant that the new run should use. - /// Options for the new thread that will be created. - /// Additional options to apply to the run that will begin. - public virtual CollectionResult CreateThreadAndRunStreaming( - Assistant assistant, - ThreadCreationOptions threadOptions = null, - RunCreationOptions runOptions = null) - => CreateThreadAndRunStreaming(assistant?.Id, threadOptions, runOptions); - - /// - /// Gets a page collection holding instances associated with an existing . - /// - /// The thread that runs in the list should be associated with. - /// Options describing the collection to return. - /// A collection of . - public virtual AsyncCollectionResult GetRunsAsync( - AssistantThread thread, - RunCollectionOptions options = default) - { - Argument.AssertNotNull(thread, nameof(thread)); - - return GetRunsAsync(thread.Id, options); - } - - /// - /// Gets a page collection holding instances associated with an existing . - /// - /// The thread that runs in the list should be associated with. - /// Options describing the collection to return. - /// A collection of . - public virtual CollectionResult GetRuns( - AssistantThread thread, - RunCollectionOptions options = default) - { - Argument.AssertNotNull(thread, nameof(thread)); - - return GetRuns(thread.Id, options); - } - - /// - /// Gets a refreshed instance of an existing . - /// - /// The run to get a refreshed instance of. - /// A new instance with updated information. - public virtual Task> GetRunAsync(ThreadRun run) - => GetRunAsync(run?.ThreadId, run?.Id); - - /// - /// Gets a refreshed instance of an existing . - /// - /// The run to get a refreshed instance of. - /// A new instance with updated information. - public virtual ClientResult GetRun(ThreadRun run) - => GetRun(run?.ThreadId, run?.Id); - - /// - /// Submits a collection of required tool call outputs to a run and resumes the run. - /// - /// The run that reached a requires_action status. - /// - /// The tool outputs, corresponding to instances from the run. - /// - /// The , updated after the submission was processed. - public virtual Task> SubmitToolOutputsToRunAsync( - ThreadRun run, - IEnumerable toolOutputs) - => SubmitToolOutputsToRunAsync(run?.ThreadId, run?.Id, toolOutputs); - - /// - /// Submits a collection of required tool call outputs to a run and resumes the run. - /// - /// The run that reached a requires_action status. - /// - /// The tool outputs, corresponding to instances from the run. - /// - /// The , updated after the submission was processed. - public virtual ClientResult SubmitToolOutputsToRun( - ThreadRun run, - IEnumerable toolOutputs) - => SubmitToolOutputsToRun(run?.ThreadId, run?.Id, toolOutputs); - - /// - /// Submits a collection of required tool call outputs to a run and resumes the run with streaming enabled. - /// - /// The run that reached a requires_action status. - /// - /// The tool outputs, corresponding to instances from the run. - /// - public virtual AsyncCollectionResult SubmitToolOutputsToRunStreamingAsync( - ThreadRun run, - IEnumerable toolOutputs) - => SubmitToolOutputsToRunStreamingAsync(run?.ThreadId, run?.Id, toolOutputs); - - /// - /// Submits a collection of required tool call outputs to a run and resumes the run with streaming enabled. - /// - /// The run that reached a requires_action status. - /// - /// The tool outputs, corresponding to instances from the run. - /// - public virtual CollectionResult SubmitToolOutputsToRunStreaming( - ThreadRun run, - IEnumerable toolOutputs) - => SubmitToolOutputsToRunStreaming(run?.ThreadId, run?.Id, toolOutputs); - - /// - /// Cancels an in-progress . - /// - /// The run to cancel. - /// An updated instance, reflecting the new status of the run. - public virtual Task> CancelRunAsync(ThreadRun run) - => CancelRunAsync(run?.ThreadId, run?.Id); - - /// - /// Cancels an in-progress . - /// - /// The run to cancel. - /// An updated instance, reflecting the new status of the run. - public virtual ClientResult CancelRun(ThreadRun run) - => CancelRun(run?.ThreadId, run?.Id); - - /// - /// Gets a page collection holding instances associated with a . - /// - /// The run to list run steps from. - /// Options describing the collection to return. - /// A collection of . - public virtual AsyncCollectionResult GetRunStepsAsync( - ThreadRun run, - RunStepCollectionOptions options = default) - { - Argument.AssertNotNull(run, nameof(run)); - - return GetRunStepsAsync(run.ThreadId, run.Id, options); - } - - /// - /// Gets a page collection holding instances associated with a . - /// - /// The run to list run steps from. - /// Options describing the collection to return. - /// A collection of . - public virtual CollectionResult GetRunSteps( - ThreadRun run, - RunStepCollectionOptions options = default) - { - Argument.AssertNotNull(run, nameof(run)); - - return GetRunSteps(run.ThreadId, run.Id, options); - } -} diff --git a/src/Custom/Assistants/AssistantClient.cs b/src/Custom/Assistants/AssistantClient.cs index 22bdb229..d3129039 100644 --- a/src/Custom/Assistants/AssistantClient.cs +++ b/src/Custom/Assistants/AssistantClient.cs @@ -44,15 +44,6 @@ public partial class AssistantClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public AssistantClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. diff --git a/src/Custom/Audio/AudioClient.cs b/src/Custom/Audio/AudioClient.cs index 9da550e8..fbe3e0c3 100644 --- a/src/Custom/Audio/AudioClient.cs +++ b/src/Custom/Audio/AudioClient.cs @@ -40,17 +40,6 @@ public partial class AudioClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public AudioClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. diff --git a/src/Custom/Audio/AudioTranscription.Serialization.cs b/src/Custom/Audio/AudioTranscription.Serialization.cs index 7f717d9c..0e73332c 100644 --- a/src/Custom/Audio/AudioTranscription.Serialization.cs +++ b/src/Custom/Audio/AudioTranscription.Serialization.cs @@ -1,5 +1,6 @@ using System; using System.ClientModel.Primitives; +using System.Collections.Generic; using System.Text.Json; namespace OpenAI.Audio; @@ -13,13 +14,13 @@ internal static AudioTranscription FromResponse(PipelineResponse response) contentType.StartsWith("text/plain", StringComparison.Ordinal)) { return new AudioTranscription( - InternalCreateTranscriptionResponseVerboseJsonTask.Transcribe, + task: default, language: null, duration: null, text: response.Content?.ToString(), - words: [], - segments: [], - serializedAdditionalRawData: new ChangeTrackingDictionary()); + words: new ChangeTrackingList(), + segments: new ChangeTrackingList(), + serializedAdditionalRawData: new Dictionary()); } using var document = JsonDocument.Parse(response.Content); diff --git a/src/Custom/Audio/AudioTranscriptionFormat.cs b/src/Custom/Audio/AudioTranscriptionFormat.cs index 77a9a721..4f38552c 100644 --- a/src/Custom/Audio/AudioTranscriptionFormat.cs +++ b/src/Custom/Audio/AudioTranscriptionFormat.cs @@ -2,30 +2,40 @@ namespace OpenAI.Audio; -/// -/// Specifies the format of the audio transcription. -/// +// CUSTOM: Renamed. +/// The format of the transcription. [CodeGenModel("CreateTranscriptionRequestResponseFormat1")] -public enum AudioTranscriptionFormat +public readonly partial struct AudioTranscriptionFormat { - /// Text. - [CodeGenMember("Text")] + // CUSTOM: + // - Applied the EditorBrowsable attribute. + // - Added custom doc comments. + /// Plain text only. [EditorBrowsable(EditorBrowsableState.Never)] - Text, + [CodeGenMember("Text")] + public static AudioTranscriptionFormat Text { get; } = new AudioTranscriptionFormat(TextValue); - /// Simple. + // CUSTOM: + // - Renamed. + // - Added custom doc comments. + /// Plain text only. [CodeGenMember("Json")] - Simple, + public static AudioTranscriptionFormat Simple { get; } = new AudioTranscriptionFormat(SimpleValue); - /// Verbose. + // CUSTOM: + // - Renamed. + // - Added custom doc comments. + /// Plain text provided with additional metadata, such as duration and timestamps. [CodeGenMember("VerboseJson")] - Verbose, + public static AudioTranscriptionFormat Verbose { get; } = new AudioTranscriptionFormat(VerboseValue); - /// SRT. + // CUSTOM: Added custom doc comments. + /// Text formatted as SubRip (.srt) file. [CodeGenMember("Srt")] - Srt, + public static AudioTranscriptionFormat Srt { get; } = new AudioTranscriptionFormat(SrtValue); - /// VTT. + // CUSTOM: Added custom doc comments. + /// Text formatted as a Web Video Text Tracks, a.k.a. WebVTT, (.vtt) file. [CodeGenMember("Vtt")] - Vtt, + public static AudioTranscriptionFormat Vtt { get; } = new AudioTranscriptionFormat(VttValue); } \ No newline at end of file diff --git a/src/Custom/Audio/AudioTranscriptionOptions.cs b/src/Custom/Audio/AudioTranscriptionOptions.cs index b13679e0..1230af13 100644 --- a/src/Custom/Audio/AudioTranscriptionOptions.cs +++ b/src/Custom/Audio/AudioTranscriptionOptions.cs @@ -1,7 +1,7 @@ -using OpenAI.Internal; using System; using System.Collections.Generic; using System.IO; +using System.Security.Cryptography; namespace OpenAI.Audio; @@ -10,69 +10,15 @@ namespace OpenAI.Audio; public partial class AudioTranscriptionOptions { // CUSTOM: Made internal. This value comes from a parameter on the client method. - /// - /// The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, - /// mpeg, mpga, m4a, ogg, pcm, wav, or webm. - /// - /// To assign a byte[] to this property use . - /// The byte[] will be serialized to a Base64 encoded string. - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromBytes(new byte[] { 1, 2, 3 }) - /// Creates a payload of "AQID". - /// - /// - /// - /// internal BinaryData File { get; } // CUSTOM: // - Made internal. The model is specified by the client. // - Added setter. - /// - /// ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) - /// is currently available. - /// internal InternalCreateTranscriptionRequestModel Model { get; set; } - // CUSTOM: Made internal. The model is specified by the client. - /// - /// The timestamp granularities to populate for this transcription. `response_format` must be set - /// `verbose_json` to use timestamp granularities. Either or both of these options are supported: - /// `word`, or `segment`. Note: There is no additional latency for segment timestamps, but - /// generating word timestamps incurs additional latency. - /// - /// To assign an object to the element of this property use . - /// - /// - /// To assign an already formatted json string to this property use . - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromObjectAsJson("foo") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromString("\"foo\"") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromObjectAsJson(new { key = "value" }) - /// Creates a payload of { "key": "value" }. - /// - /// - /// BinaryData.FromString("{\"key\": \"value\"}") - /// Creates a payload of { "key": "value" }. - /// - /// - /// - /// - internal IList TimestampGranularities { get; } + [CodeGenMember("TimestampGranularities")] + internal IList InternalTimestampGranularities { get; } // CUSTOM: Made public now that there are no required properties. /// Initializes a new instance of . @@ -83,7 +29,7 @@ public AudioTranscriptionOptions() /// /// The timestamp granularities to populate for this transcription. /// - public AudioTimestampGranularities Granularities { get; set; } + public AudioTimestampGranularities TimestampGranularities { get; set; } internal MultipartFormDataBinaryContent ToMultipartContent(Stream audio, string audioFilename) { @@ -104,16 +50,7 @@ internal MultipartFormDataBinaryContent ToMultipartContent(Stream audio, string if (ResponseFormat is not null) { - string value = ResponseFormat switch - { - AudioTranscriptionFormat.Simple => "json", - AudioTranscriptionFormat.Verbose => "verbose_json", - AudioTranscriptionFormat.Srt => "srt", - AudioTranscriptionFormat.Vtt => "vtt", - _ => throw new ArgumentException(nameof(ResponseFormat)) - }; - - content.Add(value, "response_format"); + content.Add(ResponseFormat.ToString(), "response_format"); } if (Temperature is not null) @@ -121,12 +58,12 @@ internal MultipartFormDataBinaryContent ToMultipartContent(Stream audio, string content.Add(Temperature.Value, "temperature"); } - if (Granularities.HasFlag(AudioTimestampGranularities.Word)) + if (TimestampGranularities.HasFlag(AudioTimestampGranularities.Word)) { content.Add("word", "timestamp_granularities[]"); } - if (Granularities.HasFlag(AudioTimestampGranularities.Segment)) + if (TimestampGranularities.HasFlag(AudioTimestampGranularities.Segment)) { content.Add("segment", "timestamp_granularities[]"); } diff --git a/src/Custom/Audio/AudioTranslation.Serialization.cs b/src/Custom/Audio/AudioTranslation.Serialization.cs index 63709984..a2ac14d5 100644 --- a/src/Custom/Audio/AudioTranslation.Serialization.cs +++ b/src/Custom/Audio/AudioTranslation.Serialization.cs @@ -1,5 +1,6 @@ using System; using System.ClientModel.Primitives; +using System.Collections.Generic; using System.Text.Json; namespace OpenAI.Audio; @@ -13,12 +14,12 @@ internal static AudioTranslation FromResponse(PipelineResponse response) contentType.StartsWith("text/plain", StringComparison.Ordinal)) { return new AudioTranslation( - InternalCreateTranslationResponseVerboseJsonTask.Translate, + task: default, language: null, duration: null, text: response.Content?.ToString(), - segments: [], - serializedAdditionalRawData: new ChangeTrackingDictionary()); + segments: new ChangeTrackingList(), + serializedAdditionalRawData: new Dictionary()); } using var document = JsonDocument.Parse(response.Content); diff --git a/src/Custom/Audio/AudioTranslationFormat.cs b/src/Custom/Audio/AudioTranslationFormat.cs index 84fd6686..b31457f5 100644 --- a/src/Custom/Audio/AudioTranslationFormat.cs +++ b/src/Custom/Audio/AudioTranslationFormat.cs @@ -2,30 +2,40 @@ namespace OpenAI.Audio; -/// -/// Specifies the format of the audio translation. -/// +// CUSTOM: Renamed. +/// The format of the translation. [CodeGenModel("CreateTranslationRequestResponseFormat")] -public enum AudioTranslationFormat +public readonly partial struct AudioTranslationFormat { - /// Text. - [CodeGenMember("Text")] + // CUSTOM: + // - Applied the EditorBrowsable attribute. + // - Added custom doc comments. + /// Plain text only. [EditorBrowsable(EditorBrowsableState.Never)] - Text, + [CodeGenMember("Text")] + public static AudioTranslationFormat Text { get; } = new AudioTranslationFormat(TextValue); - /// Simple. + // CUSTOM: + // - Renamed. + // - Added custom doc comments. + /// Plain text only. [CodeGenMember("Json")] - Simple, + public static AudioTranslationFormat Simple { get; } = new AudioTranslationFormat(SimpleValue); - /// Verbose. + // CUSTOM: + // - Renamed. + // - Added custom doc comments. + /// Plain text provided with additional metadata, such as duration and timestamps. [CodeGenMember("VerboseJson")] - Verbose, + public static AudioTranslationFormat Verbose { get; } = new AudioTranslationFormat(VerboseValue); - /// SRT. + // CUSTOM: Added custom doc comments. + /// Text formatted as SubRip (.srt) file. [CodeGenMember("Srt")] - Srt, + public static AudioTranslationFormat Srt { get; } = new AudioTranslationFormat(SrtValue); - /// VTT. + // CUSTOM: Added custom doc comments. + /// Text formatted as a Web Video Text Tracks, a.k.a. WebVTT, (.vtt) file. [CodeGenMember("Vtt")] - Vtt, + public static AudioTranslationFormat Vtt { get; } = new AudioTranslationFormat(VttValue); } \ No newline at end of file diff --git a/src/Custom/Audio/AudioTranslationOptions.cs b/src/Custom/Audio/AudioTranslationOptions.cs index 7632ed9c..14acc2aa 100644 --- a/src/Custom/Audio/AudioTranslationOptions.cs +++ b/src/Custom/Audio/AudioTranslationOptions.cs @@ -1,6 +1,3 @@ -using OpenAI.Embeddings; -using OpenAI.Images; -using OpenAI.Internal; using System; using System.IO; @@ -11,32 +8,11 @@ namespace OpenAI.Audio; public partial class AudioTranslationOptions { // CUSTOM: Made internal. This value comes from a parameter on the client method. - /// - /// The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, - /// mpeg, mpga, m4a, ogg, pcm, wav, or webm. - /// - /// To assign a byte[] to this property use . - /// The byte[] will be serialized to a Base64 encoded string. - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromBytes(new byte[] { 1, 2, 3 }) - /// Creates a payload of "AQID". - /// - /// - /// - /// internal BinaryData File { get; } // CUSTOM: // - Made internal. The model is specified by the client. // - Added setter. - /// - /// ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) - /// is currently available. - /// internal InternalCreateTranslationRequestModel Model { get; set; } // CUSTOM: Made public now that there are no required properties. @@ -59,16 +35,7 @@ internal MultipartFormDataBinaryContent ToMultipartContent(Stream audio, string if (ResponseFormat is not null) { - string value = ResponseFormat switch - { - AudioTranslationFormat.Simple => "json", - AudioTranslationFormat.Verbose => "verbose_json", - AudioTranslationFormat.Srt => "srt", - AudioTranslationFormat.Vtt => "vtt", - _ => throw new ArgumentException(nameof(ResponseFormat)) - }; - - content.Add(value, "response_format"); + content.Add(ResponseFormat.ToString(), "response_format"); } return content; diff --git a/src/Custom/Audio/OpenAIAudioModelFactory.cs b/src/Custom/Audio/OpenAIAudioModelFactory.cs index 6d05eaeb..f854dc3a 100644 --- a/src/Custom/Audio/OpenAIAudioModelFactory.cs +++ b/src/Custom/Audio/OpenAIAudioModelFactory.cs @@ -41,17 +41,15 @@ public static AudioTranslation AudioTranslation(string language = null, TimeSpan /// Initializes a new instance of . /// A new instance for mocking. - public static TranscribedSegment TranscribedSegment(int id = default, int seekOffset = default, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, IEnumerable tokenIds = null, float temperature = default, float averageLogProbability = default, float compressionRatio = default, float noSpeechProbability = default) + public static TranscribedSegment TranscribedSegment(int id = default, int seekOffset = default, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, ReadOnlyMemory tokenIds = default, float temperature = default, float averageLogProbability = default, float compressionRatio = default, float noSpeechProbability = default) { - tokenIds ??= new List(); - return new TranscribedSegment( id, seekOffset, startTime, endTime, text, - tokenIds.ToList(), + tokenIds, temperature, averageLogProbability, compressionRatio, diff --git a/src/Custom/Audio/TranscribedSegment.cs b/src/Custom/Audio/TranscribedSegment.cs index 38937ff1..e71554a9 100644 --- a/src/Custom/Audio/TranscribedSegment.cs +++ b/src/Custom/Audio/TranscribedSegment.cs @@ -30,7 +30,7 @@ public readonly partial struct TranscribedSegment // CUSTOM: Renamed. /// The token IDs corresponding to the text content of the segment. [CodeGenMember("Tokens")] - public IReadOnlyList TokenIds { get; } + public ReadOnlyMemory TokenIds { get; } // CUSTOM: Renamed. /// The average log probability of the segment. diff --git a/src/Custom/Batch/BatchClient.Protocol.cs b/src/Custom/Batch/BatchClient.Protocol.cs index a39f4522..3bcf065f 100644 --- a/src/Custom/Batch/BatchClient.Protocol.cs +++ b/src/Custom/Batch/BatchClient.Protocol.cs @@ -1,6 +1,7 @@ using System; using System.ClientModel; using System.ClientModel.Primitives; +using System.Text.Json; using System.Threading.Tasks; namespace OpenAI.Batch; @@ -12,33 +13,58 @@ public partial class BatchClient /// /// [Protocol Method] Creates and executes a batch from an uploaded file of requests /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. - public virtual async Task CreateBatchAsync(BinaryContent content, RequestOptions options = null) + /// A that can be used to wait for + /// the operation to complete, or cancel the operation. + public virtual async Task CreateBatchAsync(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateBatchRequest(content, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + + PipelineResponse response = await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string batchId = doc.RootElement.GetProperty("id"u8).GetString(); + string status = doc.RootElement.GetProperty("status"u8).GetString(); + + CreateBatchOperation operation = this.CreateCreateBatchOperation(batchId, status, response); + return await operation.WaitUntilAsync(waitUntilCompleted, options).ConfigureAwait(false); } /// /// [Protocol Method] Creates and executes a batch from an uploaded file of requests /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. - public virtual ClientResult CreateBatch(BinaryContent content, RequestOptions options = null) + /// A that can be used to wait for + /// the operation to complete, or cancel the operation. + public virtual CreateBatchOperation CreateBatch(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateBatchRequest(content, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + PipelineResponse response = _pipeline.ProcessMessage(message, options); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string batchId = doc.RootElement.GetProperty("id"u8).GetString(); + string status = doc.RootElement.GetProperty("status"u8).GetString(); + + CreateBatchOperation operation = this.CreateCreateBatchOperation(batchId, status, response); + return operation.WaitUntil(waitUntilCompleted, options); } /// @@ -76,7 +102,7 @@ public virtual CollectionResult GetBatches(string after, int? limit, RequestOpti /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task GetBatchAsync(string batchId, RequestOptions options) + internal virtual async Task GetBatchAsync(string batchId, RequestOptions options) { Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); @@ -93,7 +119,7 @@ public virtual async Task GetBatchAsync(string batchId, RequestOpt /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult GetBatch(string batchId, RequestOptions options) + internal virtual ClientResult GetBatch(string batchId, RequestOptions options) { Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); @@ -101,37 +127,76 @@ public virtual ClientResult GetBatch(string batchId, RequestOptions options) return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); } - /// - /// [Protocol Method] Cancels an in-progress batch. - /// - /// The ID of the batch to cancel. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual async Task CancelBatchAsync(string batchId, RequestOptions options) + internal virtual PipelineMessage CreateCreateBatchRequest(BinaryContent content, RequestOptions options) { - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; + } - using PipelineMessage message = CreateCancelBatchRequest(batchId, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + internal virtual PipelineMessage CreateGetBatchesRequest(string after, int? limit, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches", false); + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } - /// - /// [Protocol Method] Cancels an in-progress batch. - /// - /// The ID of the batch to cancel. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual ClientResult CancelBatch(string batchId, RequestOptions options) + internal virtual PipelineMessage CreateRetrieveBatchRequest(string batchId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches/", false); + uri.AppendPath(batchId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } - using PipelineMessage message = CreateCancelBatchRequest(batchId, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + internal virtual PipelineMessage CreateCancelBatchRequest(string batchId, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches/", false); + uri.AppendPath(batchId, true); + uri.AppendPath("/cancel", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } } diff --git a/src/Custom/Batch/BatchClient.cs b/src/Custom/Batch/BatchClient.cs index 0f9a6b2a..6e114d12 100644 --- a/src/Custom/Batch/BatchClient.cs +++ b/src/Custom/Batch/BatchClient.cs @@ -16,10 +16,14 @@ namespace OpenAI.Batch; [CodeGenSuppress("BatchClient", typeof(ClientPipeline), typeof(ApiKeyCredential), typeof(Uri))] [CodeGenSuppress("CreateBatch", typeof(string), typeof(InternalCreateBatchRequestEndpoint), typeof(InternalBatchCompletionTimeframe), typeof(IReadOnlyDictionary))] [CodeGenSuppress("CreateBatchAsync", typeof(string), typeof(InternalCreateBatchRequestEndpoint), typeof(InternalBatchCompletionTimeframe), typeof(IReadOnlyDictionary))] +[CodeGenSuppress("CreateBatch", typeof(BinaryContent), typeof(RequestOptions))] +[CodeGenSuppress("CreateBatchAsync", typeof(BinaryContent), typeof(RequestOptions))] [CodeGenSuppress("RetrieveBatch", typeof(string))] [CodeGenSuppress("RetrieveBatchAsync", typeof(string))] [CodeGenSuppress("CancelBatch", typeof(string))] [CodeGenSuppress("CancelBatchAsync", typeof(string))] +[CodeGenSuppress("CancelBatch", typeof(string), typeof(RequestOptions))] +[CodeGenSuppress("CancelBatchAsync", typeof(string), typeof(RequestOptions))] [CodeGenSuppress("GetBatches", typeof(string), typeof(int?))] [CodeGenSuppress("GetBatchesAsync", typeof(string), typeof(int?))] public partial class BatchClient @@ -38,15 +42,6 @@ public partial class BatchClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public BatchClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. @@ -57,13 +52,12 @@ public BatchClient(string apiKey, OpenAIClientOptions options) : this(new ApiKey { } - // CUSTOM: - // - Used a custom pipeline. - // - Demoted the endpoint parameter to be a property in the options class. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. + /// + /// Initializes a new instance of that will use an API key when authenticating. + /// + /// The API key used to authenticate with the service endpoint. + /// Additional options to customize the client. + /// The provided was null. public BatchClient(ApiKeyCredential credential, OpenAIClientOptions options) { Argument.AssertNotNull(credential, nameof(credential)); @@ -89,4 +83,9 @@ protected internal BatchClient(ClientPipeline pipeline, OpenAIClientOptions opti _pipeline = pipeline; _endpoint = OpenAIClient.GetEndpoint(options); } + + internal virtual CreateBatchOperation CreateCreateBatchOperation(string batchId, string status, PipelineResponse response) + { + return new CreateBatchOperation(_pipeline, _endpoint, batchId, status, response); + } } diff --git a/src/Custom/Batch/CreateBatchOperation.Protocol.cs b/src/Custom/Batch/CreateBatchOperation.Protocol.cs new file mode 100644 index 00000000..2a096567 --- /dev/null +++ b/src/Custom/Batch/CreateBatchOperation.Protocol.cs @@ -0,0 +1,238 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.Batch; + +/// +/// A long-running operation for executing a batch from an uploaded file of +/// requests. +/// +[Experimental("OPENAI001")] +public class CreateBatchOperation : OperationResult +{ + private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; + + private readonly string _batchId; + + internal CreateBatchOperation( + ClientPipeline pipeline, + Uri endpoint, + string batchId, + string status, + PipelineResponse response) + : base(response) + { + _pipeline = pipeline; + _endpoint = endpoint; + _batchId = batchId; + + HasCompleted = GetHasCompleted(status); + RehydrationToken = new CreateBatchOperationToken(batchId); + } + + public string BatchId => _batchId; + + /// + public override ContinuationToken? RehydrationToken { get; protected set; } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(BatchClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateBatchOperationToken token = CreateBatchOperationToken.FromToken(rehydrationToken); + + ClientResult result = await client.GetBatchAsync(token.BatchId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateBatchOperation(token.BatchId, status, response); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static CreateBatchOperation Rehydrate(BatchClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateBatchOperationToken token = CreateBatchOperationToken.FromToken(rehydrationToken); + + ClientResult result = client.GetBatch(token.BatchId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateBatchOperation(token.BatchId, status, response); + } + + /// + public override async ValueTask UpdateStatusAsync(RequestOptions? options = null) + { + ClientResult result = await GetBatchAsync(options).ConfigureAwait(false); + + ApplyUpdate(result); + + return result; + } + + /// + public override ClientResult UpdateStatus(RequestOptions? options = null) + { + ClientResult result = GetBatch(options); + + ApplyUpdate(result); + + return result; + } + + internal async Task WaitUntilAsync(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + await WaitForCompletionAsync(options?.CancellationToken ?? default).ConfigureAwait(false); + return this; + } + + internal CreateBatchOperation WaitUntil(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + WaitForCompletion(options?.CancellationToken ?? default); + return this; + } + + private void ApplyUpdate(ClientResult result) + { + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string? status = doc.RootElement.GetProperty("status"u8).GetString(); + + HasCompleted = GetHasCompleted(status); + SetRawResponse(response); + } + + private static bool GetHasCompleted(string? status) + { + return status == InternalBatchStatus.Completed || + status == InternalBatchStatus.Cancelled || + status == InternalBatchStatus.Expired || + status == InternalBatchStatus.Failed; + } + + // Generated protocol methods + + /// + /// [Protocol Method] Retrieves a batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual async Task GetBatchAsync(RequestOptions? options) + { + using PipelineMessage message = CreateRetrieveBatchRequest(_batchId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Retrieves a batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual ClientResult GetBatch(RequestOptions? options) + { + using PipelineMessage message = CreateRetrieveBatchRequest(_batchId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + /// + /// [Protocol Method] Cancels an in-progress batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual async Task CancelAsync(RequestOptions? options) + { + using PipelineMessage message = CreateCancelBatchRequest(_batchId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Cancels an in-progress batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual ClientResult Cancel(RequestOptions? options) + { + using PipelineMessage message = CreateCancelBatchRequest(_batchId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + internal virtual PipelineMessage CreateRetrieveBatchRequest(string batchId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches/", false); + uri.AppendPath(batchId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateCancelBatchRequest(string batchId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/batches/", false); + uri.AppendPath(batchId, true); + uri.AppendPath("/cancel", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} \ No newline at end of file diff --git a/src/Custom/Batch/Internal/CreateBatchOperationToken.cs b/src/Custom/Batch/Internal/CreateBatchOperationToken.cs new file mode 100644 index 00000000..78b0d61c --- /dev/null +++ b/src/Custom/Batch/Internal/CreateBatchOperationToken.cs @@ -0,0 +1,90 @@ +using System; +using System.ClientModel; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +#nullable enable + +namespace OpenAI.Batch; + +internal class CreateBatchOperationToken : ContinuationToken +{ + public CreateBatchOperationToken(string batchId) + { + BatchId = batchId; + } + + public string BatchId { get; } + + public override BinaryData ToBytes() + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + writer.WriteStartObject(); + + writer.WriteString("batchId", BatchId); + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); + } + + public static CreateBatchOperationToken FromToken(ContinuationToken continuationToken) + { + if (continuationToken is CreateBatchOperationToken token) + { + return token; + } + + BinaryData data = continuationToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create CreateBatchOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + Utf8JsonReader reader = new(data); + + string batchId = null!; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "batchId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + batchId = reader.GetString()!; + break; + + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + if (batchId is null) + { + throw new ArgumentException("Failed to create CreateBatchOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + return new(batchId); + } +} + diff --git a/src/Custom/Chat/AssistantChatMessage.cs b/src/Custom/Chat/AssistantChatMessage.cs index b9322d04..db2b80de 100644 --- a/src/Custom/Chat/AssistantChatMessage.cs +++ b/src/Custom/Chat/AssistantChatMessage.cs @@ -61,7 +61,7 @@ public AssistantChatMessage(string content) public AssistantChatMessage(IEnumerable toolCalls) : base(ChatMessageRole.Assistant) { - Argument.AssertNotNull(toolCalls, nameof(toolCalls)); + Argument.AssertNotNullOrEmpty(toolCalls, nameof(toolCalls)); foreach (ChatToolCall toolCall in toolCalls) { @@ -74,6 +74,7 @@ public AssistantChatMessage(IEnumerable toolCalls) /// (deprecated in favor of tool_calls) that was made by the model. /// /// The function_call made by the model. + [Obsolete($"This constructor is obsolete. Please use the constructor that takes an IEnumerable parameter instead.")] public AssistantChatMessage(ChatFunctionCall functionCall) : base(ChatMessageRole.Assistant) { diff --git a/src/Custom/Chat/ChatClient.cs b/src/Custom/Chat/ChatClient.cs index 54a94f93..b8c70d1c 100644 --- a/src/Custom/Chat/ChatClient.cs +++ b/src/Custom/Chat/ChatClient.cs @@ -39,17 +39,6 @@ public partial class ChatClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public ChatClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. @@ -216,7 +205,7 @@ async Task sendRequestAsync() => /// generated by the model instead of waiting for it to be finished first. /// /// - /// implements the interface and can be + /// implements the interface and can be /// enumerated over using the await foreach pattern. /// /// The messages comprising the chat so far. @@ -255,7 +244,7 @@ public virtual AsyncCollectionResult CompleteChat /// generated by the model instead of waiting for it to be finished first. /// /// - /// implements the interface and can be + /// implements the interface and can be /// enumerated over using the await foreach pattern. /// /// The messages comprising the chat so far. diff --git a/src/Custom/Chat/ChatMessage.Serialization.cs b/src/Custom/Chat/ChatMessage.Serialization.cs index 6d1ae848..549e5401 100644 --- a/src/Custom/Chat/ChatMessage.Serialization.cs +++ b/src/Custom/Chat/ChatMessage.Serialization.cs @@ -7,7 +7,7 @@ namespace OpenAI.Chat; [CodeGenSuppress("global::System.ClientModel.Primitives.IJsonModel.Write", typeof(Utf8JsonWriter), typeof(ModelReaderWriterOptions))] -public abstract partial class ChatMessage : IJsonModel +public partial class ChatMessage : IJsonModel { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void SerializeContentValue(Utf8JsonWriter writer, ModelReaderWriterOptions options = null) @@ -43,5 +43,6 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptio internal static void WriteCore(ChatMessage instance, Utf8JsonWriter writer, ModelReaderWriterOptions options) => instance.WriteCore(writer, options); - internal abstract void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options); + internal virtual void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + => throw new InvalidOperationException($"The {nameof(WriteCore)} method should be invoked on an overriding type derived from {nameof(ChatMessage)}."); } diff --git a/src/Custom/Chat/ChatMessage.cs b/src/Custom/Chat/ChatMessage.cs index 990d4b99..bfb51d38 100644 --- a/src/Custom/Chat/ChatMessage.cs +++ b/src/Custom/Chat/ChatMessage.cs @@ -54,16 +54,15 @@ namespace OpenAI.Chat; /// [CodeGenModel("ChatCompletionRequestMessage")] [CodeGenSerialization(nameof(Content), SerializationValueHook = nameof(SerializeContentValue), DeserializationValueHook = nameof(DeserializeContentValue))] -public abstract partial class ChatMessage -{ +public partial class ChatMessage +{ // CUSTOM: Changed type from string to ChatMessageRole. [CodeGenMember("Role")] internal ChatMessageRole Role { get; set; } // CUSTOM: Made internal. internal ChatMessage() - { - } + { } internal ChatMessage(ChatMessageRole role, IEnumerable contentParts) { diff --git a/src/Custom/Chat/ChatMessageContentPart.Serialization.cs b/src/Custom/Chat/ChatMessageContentPart.Serialization.cs index 506cddcf..fdfff941 100644 --- a/src/Custom/Chat/ChatMessageContentPart.Serialization.cs +++ b/src/Custom/Chat/ChatMessageContentPart.Serialization.cs @@ -1,3 +1,4 @@ +using OpenAI.Files; using System; using System.ClientModel.Primitives; using System.Collections.Generic; @@ -15,7 +16,7 @@ internal static void WriteCoreContentPart(ChatMessageContentPart instance, Utf8J { writer.WriteStartObject(); writer.WritePropertyName("type"u8); - writer.WriteStringValue(instance._kind.ToString()); + writer.WriteStringValue(instance._kind.ToSerialString()); if (instance._kind == ChatMessageContentPartKind.Text) { @@ -44,7 +45,7 @@ internal static void WriteCoreContentPartList(IList inst } writer.WritePropertyName("content"u8); - if (instances.Count == 1 && !string.IsNullOrEmpty(instances[0].Text)) + if (instances.Count == 1 && instances[0].Text != null) { writer.WriteStringValue(instances[0].Text); } @@ -68,7 +69,7 @@ internal static ChatMessageContentPart DeserializeChatMessageContentPart(JsonEle return null; } - string kind = default; + ChatMessageContentPartKind kind = default; string text = default; string refusal = default; InternalChatCompletionRequestMessageContentPartImageImageUrl imageUri = default; @@ -78,7 +79,7 @@ internal static ChatMessageContentPart DeserializeChatMessageContentPart(JsonEle { if (property.NameEquals("type"u8)) { - kind = property.Value.GetString(); + kind = property.Value.GetString().ToChatMessageContentPartKind(); continue; } if (property.NameEquals("text"u8)) diff --git a/src/Custom/Chat/ChatMessageContentPart.cs b/src/Custom/Chat/ChatMessageContentPart.cs index d4ebd372..59f12081 100644 --- a/src/Custom/Chat/ChatMessageContentPart.cs +++ b/src/Custom/Chat/ChatMessageContentPart.cs @@ -36,9 +36,9 @@ internal ChatMessageContentPart() } // CUSTOM: Added to support deserialization. - internal ChatMessageContentPart(string kind, string text, InternalChatCompletionRequestMessageContentPartImageImageUrl imageUri, string refusal, IDictionary serializedAdditionalRawData) + internal ChatMessageContentPart(ChatMessageContentPartKind kind, string text, InternalChatCompletionRequestMessageContentPartImageImageUrl imageUri, string refusal, IDictionary serializedAdditionalRawData) { - _kind = new ChatMessageContentPartKind(kind); + _kind = kind; _text = text; _imageUri = imageUri; _refusal = refusal; @@ -89,7 +89,7 @@ public static ChatMessageContentPart CreateTextPart(string text) Argument.AssertNotNull(text, nameof(text)); return new ChatMessageContentPart( - kind: ChatMessageContentPartKind.Text.ToString(), + kind: ChatMessageContentPartKind.Text, text: text, imageUri: null, refusal: null, @@ -108,7 +108,7 @@ public static ChatMessageContentPart CreateImagePart(Uri imageUri, ChatImageDeta Argument.AssertNotNull(imageUri, nameof(imageUri)); return new ChatMessageContentPart( - kind: ChatMessageContentPartKind.Image.ToString(), + kind: ChatMessageContentPartKind.Image, text: null, imageUri: new(imageUri) { Detail = imageDetailLevel }, refusal: null, @@ -130,7 +130,7 @@ public static ChatMessageContentPart CreateImagePart(BinaryData imageBytes, stri Argument.AssertNotNullOrEmpty(imageBytesMediaType, nameof(imageBytesMediaType)); return new ChatMessageContentPart( - kind: ChatMessageContentPartKind.Image.ToString(), + kind: ChatMessageContentPartKind.Image, text: null, imageUri: new(imageBytes, imageBytesMediaType) { Detail = imageDetailLevel }, refusal: null, @@ -145,7 +145,7 @@ public static ChatMessageContentPart CreateRefusalPart(string refusal) Argument.AssertNotNull(refusal, nameof(refusal)); return new ChatMessageContentPart( - kind: ChatMessageContentPartKind.Refusal.ToString(), + kind: ChatMessageContentPartKind.Refusal, text: null, imageUri: null, refusal: refusal, diff --git a/src/Custom/Chat/ChatMessageContentPartKind.Serialization.cs b/src/Custom/Chat/ChatMessageContentPartKind.Serialization.cs new file mode 100644 index 00000000..2ef688cd --- /dev/null +++ b/src/Custom/Chat/ChatMessageContentPartKind.Serialization.cs @@ -0,0 +1,26 @@ +// + +#nullable disable + +using System; + +namespace OpenAI.Chat; + +internal static partial class ChatMessageContentPartKindExtensions +{ + public static string ToSerialString(this ChatMessageContentPartKind value) => value switch + { + ChatMessageContentPartKind.Text => "text", + ChatMessageContentPartKind.Refusal => "refusal", + ChatMessageContentPartKind.Image => "image_url", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatMessageContentPartKind value.") + }; + + public static ChatMessageContentPartKind ToChatMessageContentPartKind(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "text")) return ChatMessageContentPartKind.Text; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "refusal")) return ChatMessageContentPartKind.Refusal; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "image_url")) return ChatMessageContentPartKind.Image; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatMessageContentPartKind value."); + } +} diff --git a/src/Custom/Chat/ChatMessageContentPartKind.cs b/src/Custom/Chat/ChatMessageContentPartKind.cs index 0962a4bc..a6cc4f5e 100644 --- a/src/Custom/Chat/ChatMessageContentPartKind.cs +++ b/src/Custom/Chat/ChatMessageContentPartKind.cs @@ -1,49 +1,13 @@ -using System.ComponentModel; -using System; - namespace OpenAI.Chat; /// /// Represents the possibles of underlying data for a chat message's content property. /// -public readonly partial struct ChatMessageContentPartKind : IEquatable +public enum ChatMessageContentPartKind { - private readonly string _value; - - /// Initializes a new instance of . - /// is null. - public ChatMessageContentPartKind(string value) - { - _value = value ?? throw new ArgumentNullException(nameof(value)); - } - - private const string TextValue = "text"; - private const string RefusalValue = "refusal"; - private const string ImageValue = "image_url"; - - /// Text. - public static ChatMessageContentPartKind Text { get; } = new ChatMessageContentPartKind(TextValue); - /// Refusal. - public static ChatMessageContentPartKind Refusal { get; } = new ChatMessageContentPartKind(RefusalValue); - /// Image. - public static ChatMessageContentPartKind Image { get; } = new ChatMessageContentPartKind(ImageValue); - - /// Determines if two values are the same. - public static bool operator ==(ChatMessageContentPartKind left, ChatMessageContentPartKind right) => left.Equals(right); - /// Determines if two values are not the same. - public static bool operator !=(ChatMessageContentPartKind left, ChatMessageContentPartKind right) => !left.Equals(right); - /// Converts a string to a . - public static implicit operator ChatMessageContentPartKind(string value) => new ChatMessageContentPartKind(value); + Text, - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => obj is ChatMessageContentPartKind other && Equals(other); - /// - public bool Equals(ChatMessageContentPartKind other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + Refusal, - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; - /// - public override string ToString() => _value; + Image } \ No newline at end of file diff --git a/src/Custom/Chat/ChatResponseFormat.Serialization.cs b/src/Custom/Chat/ChatResponseFormat.Serialization.cs index ff7fdeaa..cf6b9fb2 100644 --- a/src/Custom/Chat/ChatResponseFormat.Serialization.cs +++ b/src/Custom/Chat/ChatResponseFormat.Serialization.cs @@ -7,7 +7,7 @@ namespace OpenAI.Chat; [CodeGenSuppress("global::System.ClientModel.Primitives.IJsonModel.Write", typeof(Utf8JsonWriter), typeof(ModelReaderWriterOptions))] -public abstract partial class ChatResponseFormat : IJsonModel +public partial class ChatResponseFormat : IJsonModel { void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) => CustomSerializationHelpers.SerializeInstance(this, WriteCore, writer, options); @@ -15,5 +15,6 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWrit internal static void WriteCore(ChatResponseFormat instance, Utf8JsonWriter writer, ModelReaderWriterOptions options) => instance.WriteCore(writer, options); - internal abstract void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options); + internal virtual void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + => throw new InvalidOperationException($"The {nameof(WriteCore)} method should be invoked on an overriding type derived from {nameof(ChatResponseFormat)}."); } diff --git a/src/Custom/Chat/ChatResponseFormat.cs b/src/Custom/Chat/ChatResponseFormat.cs index e7123c46..817ae561 100644 --- a/src/Custom/Chat/ChatResponseFormat.cs +++ b/src/Custom/Chat/ChatResponseFormat.cs @@ -22,7 +22,7 @@ namespace OpenAI.Chat; /// /// [CodeGenModel("ChatResponseFormat")] -public abstract partial class ChatResponseFormat +public partial class ChatResponseFormat { /// Creates a new requesting plain text. public static ChatResponseFormat CreateTextFormat() => new InternalChatResponseFormatText(); @@ -103,4 +103,8 @@ public static ChatResponseFormat CreateJsonSchemaFormat(string jsonSchemaFormatN return new InternalChatResponseFormatJsonSchema(internalSchema); } + + // CUSTOM: Made internal. + internal ChatResponseFormat() + { } } \ No newline at end of file diff --git a/src/Custom/Chat/ChatToolCallKind.cs b/src/Custom/Chat/ChatToolCallKind.cs index 1a0b1716..edf4b5db 100644 --- a/src/Custom/Chat/ChatToolCallKind.cs +++ b/src/Custom/Chat/ChatToolCallKind.cs @@ -1,6 +1,7 @@ namespace OpenAI.Chat; [CodeGenModel("ChatCompletionMessageToolCallType")] -public readonly partial struct ChatToolCallKind +public enum ChatToolCallKind { + Function, } diff --git a/src/Custom/Chat/ChatToolKind.cs b/src/Custom/Chat/ChatToolKind.cs index 1bd3ca39..7d7f7a7a 100644 --- a/src/Custom/Chat/ChatToolKind.cs +++ b/src/Custom/Chat/ChatToolKind.cs @@ -1,6 +1,7 @@ namespace OpenAI.Chat; [CodeGenModel("ChatCompletionToolType")] -public readonly partial struct ChatToolKind +public enum ChatToolKind { + Function, } diff --git a/src/Custom/Embeddings/EmbeddingClient.cs b/src/Custom/Embeddings/EmbeddingClient.cs index 337a7f96..3cbc9a09 100644 --- a/src/Custom/Embeddings/EmbeddingClient.cs +++ b/src/Custom/Embeddings/EmbeddingClient.cs @@ -37,17 +37,6 @@ public partial class EmbeddingClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public EmbeddingClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. @@ -181,14 +170,14 @@ public virtual ClientResult GenerateEmbeddings(IEnume return ClientResult.FromValue(OpenAIEmbeddingCollection.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } - // CUSTOM: Added to simplify passing the input as a collection of a collection of tokens instead of BinaryData. + // CUSTOM: Added to simplify passing the input as a collection of ReadOnlyMemory tokens instead of BinaryData. /// Generates embeddings representing the text inputs. /// The text inputs to generate embeddings for. /// The options to configure the embedding generation. /// A token that can be used to cancel this method call. /// is null. /// is an empty collection, and was expected to be non-empty. - public virtual async Task> GenerateEmbeddingsAsync(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default) + public virtual async Task> GenerateEmbeddingsAsync(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default) { Argument.AssertNotNullOrEmpty(inputs, nameof(inputs)); @@ -200,14 +189,14 @@ public virtual async Task> GenerateEmbed return ClientResult.FromValue(OpenAIEmbeddingCollection.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } - // CUSTOM: Added to simplify passing the input as a collection of a collection of tokens instead of BinaryData. + // CUSTOM: Added to simplify passing the input as a collection of ReadOnlyMemory of tokens instead of BinaryData. /// Generates embeddings representing the text inputs. /// The text inputs to generate embeddings for. /// The options to configure the embedding generation. /// A token that can be used to cancel this method call. /// is null. /// is an empty collection, and was expected to be non-empty. - public virtual ClientResult GenerateEmbeddings(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default) + public virtual ClientResult GenerateEmbeddings(IEnumerable> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default) { Argument.AssertNotNullOrEmpty(inputs, nameof(inputs)); diff --git a/src/Custom/Files/FileClient.cs b/src/Custom/Files/FileClient.cs index 6b876198..fe6f0a45 100644 --- a/src/Custom/Files/FileClient.cs +++ b/src/Custom/Files/FileClient.cs @@ -43,17 +43,6 @@ public partial class FileClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public FileClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. @@ -246,18 +235,18 @@ public virtual ClientResult GetFiles(CancellationToken can /// Gets basic information about each of the files belonging to the user's organization. /// Only return files with the given purpose. /// A token that can be used to cancel this method call. - public virtual async Task> GetFilesAsync(OpenAIFilePurpose purpose, CancellationToken cancellationToken = default) + public virtual async Task> GetFilesAsync(FilePurpose purpose, CancellationToken cancellationToken = default) { - ClientResult result = await GetFilesAsync(purpose.ToString(), cancellationToken.ToRequestOptions()).ConfigureAwait(false); + ClientResult result = await GetFilesAsync(purpose.ToSerialString(), cancellationToken.ToRequestOptions()).ConfigureAwait(false); return ClientResult.FromValue(OpenAIFileCollection.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } /// Gets basic information about each of the files belonging to the user's organization. /// Only return files with the given purpose. /// A token that can be used to cancel this method call. - public virtual ClientResult GetFiles(OpenAIFilePurpose purpose, CancellationToken cancellationToken = default) + public virtual ClientResult GetFiles(FilePurpose purpose, CancellationToken cancellationToken = default) { - ClientResult result = GetFiles(purpose.ToString(), cancellationToken.ToRequestOptions()); + ClientResult result = GetFiles(purpose.ToSerialString(), cancellationToken.ToRequestOptions()); return ClientResult.FromValue(OpenAIFileCollection.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } diff --git a/src/Custom/Files/FilePurpose.cs b/src/Custom/Files/FilePurpose.cs new file mode 100644 index 00000000..a532bb85 --- /dev/null +++ b/src/Custom/Files/FilePurpose.cs @@ -0,0 +1,19 @@ +namespace OpenAI.Files; + +[CodeGenModel("OpenAIFilePurpose")] +public enum FilePurpose +{ + Assistants, + + AssistantsOutput, + + Batch, + + BatchOutput, + + FineTune, + + FineTuneResults, + + Vision, +} \ No newline at end of file diff --git a/src/Custom/Files/OpenAIFileStatus.cs b/src/Custom/Files/FileStatus.cs similarity index 86% rename from src/Custom/Files/OpenAIFileStatus.cs rename to src/Custom/Files/FileStatus.cs index cfff790a..4582490b 100644 --- a/src/Custom/Files/OpenAIFileStatus.cs +++ b/src/Custom/Files/FileStatus.cs @@ -7,6 +7,11 @@ namespace OpenAI.Files; + $" after it has been uploaded. While the file is processing, you can still create a fine-tuning job but it" + $" will not start until the file processing has completed.")] [CodeGenModel("OpenAIFileStatus")] -public readonly partial struct OpenAIFileStatus +public enum FileStatus { + Uploaded, + + Processed, + + Error, } \ No newline at end of file diff --git a/src/Custom/Files/OpenAIFile.cs b/src/Custom/Files/OpenAIFile.cs index ce4e34b7..6ac47142 100644 --- a/src/Custom/Files/OpenAIFile.cs +++ b/src/Custom/Files/OpenAIFile.cs @@ -18,7 +18,7 @@ public partial class OpenAIFile [Obsolete($"This property is obsolete. If this is a fine-tuning training file, it may take some time to process" + $" after it has been uploaded. While the file is processing, you can still create a fine-tuning job but it" + $" will not start until the file processing has completed.")] - public OpenAIFileStatus Status { get; } + public FileStatus Status { get; } // CUSTOM: Added the Obsolete attribute. [Obsolete($"This property is obsolete. For details on why a fine-tuning training file failed validation, see the" diff --git a/src/Custom/Files/OpenAIFilePurpose.cs b/src/Custom/Files/OpenAIFilePurpose.cs deleted file mode 100644 index 2d659b99..00000000 --- a/src/Custom/Files/OpenAIFilePurpose.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OpenAI.Files; - -[CodeGenModel("OpenAIFilePurpose")] -public readonly partial struct OpenAIFilePurpose -{ -} \ No newline at end of file diff --git a/src/Custom/Files/OpenAIFilesModelFactory.cs b/src/Custom/Files/OpenAIFilesModelFactory.cs index edb573b5..115fdcca 100644 --- a/src/Custom/Files/OpenAIFilesModelFactory.cs +++ b/src/Custom/Files/OpenAIFilesModelFactory.cs @@ -20,7 +20,7 @@ public static FileDeletionResult FileDeletionResult(string fileId = null, bool d /// Initializes a new instance of . /// A new instance for mocking. - public static OpenAIFile OpenAIFileInfo(string id = null, int? sizeInBytes = null, DateTimeOffset createdAt = default, string filename = null, OpenAIFilePurpose purpose = default, OpenAIFileStatus status = default, string statusDetails = null) + public static OpenAIFile OpenAIFileInfo(string id = null, int? sizeInBytes = null, DateTimeOffset createdAt = default, string filename = null, FilePurpose purpose = default, FileStatus status = default, string statusDetails = null) { return new OpenAIFile( id, diff --git a/src/Custom/FineTuning/FineTuningClient.Protocol.cs b/src/Custom/FineTuning/FineTuningClient.Protocol.cs index 513a00f7..fe8f8a6b 100644 --- a/src/Custom/FineTuning/FineTuningClient.Protocol.cs +++ b/src/Custom/FineTuning/FineTuningClient.Protocol.cs @@ -1,7 +1,7 @@ using System; using System.ClientModel; using System.ClientModel.Primitives; -using System.Collections.Generic; +using System.Text.Json; using System.Threading.Tasks; namespace OpenAI.FineTuning; @@ -30,17 +30,33 @@ public partial class FineTuningClient /// /// [Learn more about fine-tuning](/docs/guides/fine-tuning) /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. - public virtual async Task CreateJobAsync(BinaryContent content, RequestOptions options = null) + /// A that can be used to wait for + /// the operation to complete, get information about the fine tuning job, or + /// cancel the operation. + public virtual async Task CreateFineTuningJobAsync( + BinaryContent content, + bool waitUntilCompleted, + RequestOptions options = null) { Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateFineTuningJobRequest(content, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + PipelineResponse response = await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string jobId = doc.RootElement.GetProperty("id"u8).GetString(); + string status = doc.RootElement.GetProperty("status"u8).GetString(); + + FineTuningJobOperation operation = this.CreateCreateJobOperation(jobId, status, response); + return await operation.WaitUntilAsync(waitUntilCompleted, options).ConfigureAwait(false); } // CUSTOM: @@ -53,17 +69,33 @@ public virtual async Task CreateJobAsync(BinaryContent content, Re /// /// [Learn more about fine-tuning](/docs/guides/fine-tuning) /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. - public virtual ClientResult CreateJob(BinaryContent content, RequestOptions options = null) + /// A that can be used to wait for + /// the operation to complete, get information about the fine tuning job, or + /// cancel the operation. + public virtual FineTuningJobOperation CreateFineTuningJob( + BinaryContent content, + bool waitUntilCompleted, + RequestOptions options = null) { Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateFineTuningJobRequest(content, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + PipelineResponse response = _pipeline.ProcessMessage(message, options); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string jobId = doc.RootElement.GetProperty("id"u8).GetString(); + string status = doc.RootElement.GetProperty("status"u8).GetString(); + + FineTuningJobOperation operation = this.CreateCreateJobOperation(jobId, status, response); + return operation.WaitUntil(waitUntilCompleted, options); } // CUSTOM: @@ -106,17 +138,17 @@ public virtual CollectionResult GetJobs(string after, int? limit, RequestOptions /// /// [Learn more about fine-tuning](/docs/guides/fine-tuning) /// - /// The ID of the fine-tuning job. + /// The ID of the fine-tuning job. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. + /// is null. + /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task GetJobAsync(string jobId, RequestOptions options) + public virtual async Task GetJobAsync(string fineTuningJobId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); + Argument.AssertNotNullOrEmpty(fineTuningJobId, nameof(fineTuningJobId)); - using PipelineMessage message = CreateRetrieveFineTuningJobRequest(jobId, options); + using PipelineMessage message = CreateRetrieveFineTuningJobRequest(fineTuningJobId, options); return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } @@ -128,136 +160,73 @@ public virtual async Task GetJobAsync(string jobId, RequestOptions /// /// [Learn more about fine-tuning](/docs/guides/fine-tuning) /// - /// The ID of the fine-tuning job. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual ClientResult GetJob(string jobId, RequestOptions options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = CreateRetrieveFineTuningJobRequest(jobId, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); - } - - // CUSTOM: - // - Renamed. - // - Edited doc comment. - /// - /// [Protocol Method] Immediately cancel a fine-tune job. - /// - /// The ID of the fine-tuning job to cancel. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual async Task CancelJobAsync(string jobId, RequestOptions options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = CreateCancelFineTuningJobRequest(jobId, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); - } - - // CUSTOM: - // - Renamed. - // - Edited doc comment. - /// - /// [Protocol Method] Immediately cancel a fine-tune job. - /// - /// The ID of the fine-tuning job to cancel. + /// The ID of the fine-tuning job. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. + /// is null. + /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult CancelJob(string jobId, RequestOptions options) + public virtual ClientResult GetJob(string fineTuningJobId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); + Argument.AssertNotNullOrEmpty(fineTuningJobId, nameof(fineTuningJobId)); - using PipelineMessage message = CreateCancelFineTuningJobRequest(jobId, options); + using PipelineMessage message = CreateRetrieveFineTuningJobRequest(fineTuningJobId, options); return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); } - // CUSTOM: - // - Renamed. - // - Edited doc comment. - /// - /// [Protocol Method] Get status updates for a fine-tuning job. - /// - /// The ID of the fine-tuning job to get events for. - /// Identifier for the last event from the previous pagination request. - /// Number of events to retrieve. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual AsyncCollectionResult GetJobEventsAsync(string jobId, string after, int? limit, RequestOptions options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - return new AsyncFineTuningJobEventCollectionResult(this, _pipeline, options, jobId, limit, after); - } - - // CUSTOM: - // - Renamed. - // - Edited doc comment. - /// - /// [Protocol Method] Get status updates for a fine-tuning job. - /// - /// The ID of the fine-tuning job to get events for. - /// Identifier for the last event from the previous pagination request. - /// Number of events to retrieve. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual CollectionResult GetJobEvents(string jobId, string after, int? limit, RequestOptions options) + internal virtual PipelineMessage CreateCreateFineTuningJobRequest(BinaryContent content, RequestOptions options) { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - return new FineTuningJobEventCollectionResult(this, _pipeline, options, jobId, limit, after); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; } - /// - /// [Protocol Method] List the checkpoints for a fine-tuning job. - /// - /// The ID of the fine-tuning job to get checkpoints for. - /// Identifier for the last checkpoint ID from the previous pagination request. - /// Number of checkpoints to retrieve. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual AsyncCollectionResult GetJobCheckpointsAsync(string jobId, string after, int? limit, RequestOptions options) + internal virtual PipelineMessage CreateGetPaginatedFineTuningJobsRequest(string after, int? limit, RequestOptions options) { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - return new AsyncFineTuningJobCheckpointCollectionResult(this, _pipeline, options, jobId, limit, after); - + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs", false); + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } - /// - /// [Protocol Method] List the checkpoints for a fine-tuning job. - /// - /// The ID of the fine-tuning job to get checkpoints for. - /// Identifier for the last checkpoint ID from the previous pagination request. - /// Number of checkpoints to retrieve. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// is null. - /// is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - public virtual CollectionResult GetJobCheckpoints(string jobId, string after, int? limit, RequestOptions options) + internal virtual PipelineMessage CreateRetrieveFineTuningJobRequest(string fineTuningJobId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - return new FineTuningJobCheckpointCollectionResult(this, _pipeline, options, jobId, limit, after); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs/", false); + uri.AppendPath(fineTuningJobId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } } diff --git a/src/Custom/FineTuning/FineTuningClient.cs b/src/Custom/FineTuning/FineTuningClient.cs index ee47730a..aaf581b5 100644 --- a/src/Custom/FineTuning/FineTuningClient.cs +++ b/src/Custom/FineTuning/FineTuningClient.cs @@ -41,15 +41,6 @@ public partial class FineTuningClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public FineTuningClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. @@ -60,13 +51,12 @@ public FineTuningClient(string apiKey, OpenAIClientOptions options) : this(new A { } - // CUSTOM: - // - Used a custom pipeline. - // - Demoted the endpoint parameter to be a property in the options class. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. + /// + /// Initializes a new instance of that will use an API key when authenticating. + /// + /// The API key used to authenticate with the service endpoint. + /// Additional options to customize the client. + /// The provided was null. public FineTuningClient(ApiKeyCredential credential, OpenAIClientOptions options) { Argument.AssertNotNull(credential, nameof(credential)); @@ -92,4 +82,9 @@ protected internal FineTuningClient(ClientPipeline pipeline, OpenAIClientOptions _pipeline = pipeline; _endpoint = OpenAIClient.GetEndpoint(options); } + + internal virtual FineTuningJobOperation CreateCreateJobOperation(string jobId, string status, PipelineResponse response) + { + return new FineTuningJobOperation(_pipeline, _endpoint, jobId, status, response); + } } diff --git a/src/Custom/FineTuning/FineTuningJobOperation.Protocol.cs b/src/Custom/FineTuning/FineTuningJobOperation.Protocol.cs new file mode 100644 index 00000000..0eac14b0 --- /dev/null +++ b/src/Custom/FineTuning/FineTuningJobOperation.Protocol.cs @@ -0,0 +1,462 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.FineTuning; + +/// +/// A long-running operation for creating a new model from a given dataset. +/// +[Experimental("OPENAI001")] +public class FineTuningJobOperation : OperationResult +{ + private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; + + private readonly string _jobId; + + internal FineTuningJobOperation( + ClientPipeline pipeline, + Uri endpoint, + string jobId, + string status, + PipelineResponse response) : base(response) + { + _pipeline = pipeline; + _endpoint = endpoint; + _jobId = jobId; + + HasCompleted = GetHasCompleted(status); + RehydrationToken = new FineTuningJobOperationToken(jobId); + } + + public string JobId => _jobId; + + /// + public override ContinuationToken? RehydrationToken { get; protected set; } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(FineTuningClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + FineTuningJobOperationToken token = FineTuningJobOperationToken.FromToken(rehydrationToken); + + ClientResult result = await client.GetJobAsync(token.JobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateJobOperation(token.JobId, status, response); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static FineTuningJobOperation Rehydrate(FineTuningClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + FineTuningJobOperationToken token = FineTuningJobOperationToken.FromToken(rehydrationToken); + + ClientResult result = client.GetJob(token.JobId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateJobOperation(token.JobId, status, response); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The id of the fine tuning job to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(FineTuningClient client, string fineTuningJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(fineTuningJobId, nameof(fineTuningJobId)); + + ClientResult result = await client.GetJobAsync(fineTuningJobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateJobOperation(fineTuningJobId, status, response); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The id of the fine tuning job to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static FineTuningJobOperation Rehydrate(FineTuningClient client, string fineTuningJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(fineTuningJobId, nameof(fineTuningJobId)); + + ClientResult result = client.GetJob(fineTuningJobId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string status = doc.RootElement.GetProperty("status"u8).GetString()!; + + return client.CreateCreateJobOperation(fineTuningJobId, status, response); + } + + /// + public override async ValueTask UpdateStatusAsync(RequestOptions? options = null) + { + ClientResult result = await GetJobAsync(options).ConfigureAwait(false); + + ApplyUpdate(result); + + return result; + } + + /// + public override ClientResult UpdateStatus(RequestOptions? options = null) + { + ClientResult result = GetJob(options); + + ApplyUpdate(result); + + return result; + } + + internal async Task WaitUntilAsync(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + await WaitForCompletionAsync(options?.CancellationToken ?? default).ConfigureAwait(false); + return this; + } + + internal FineTuningJobOperation WaitUntil(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + WaitForCompletion(options?.CancellationToken ?? default); + return this; + } + + private void ApplyUpdate(ClientResult result) + { + PipelineResponse response = result.GetRawResponse(); + + using JsonDocument doc = JsonDocument.Parse(response.Content); + string? status = doc.RootElement.GetProperty("status"u8).GetString(); + + HasCompleted = GetHasCompleted(status); + SetRawResponse(response); + } + + private static bool GetHasCompleted(string? status) + { + return status == FineTuningJobStatus.Succeeded || + status == FineTuningJobStatus.Failed || + status == FineTuningJobStatus.Cancelled; + } + + // Generated protocol methods + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Get info about a fine-tuning job. + /// + /// [Learn more about fine-tuning](/docs/guides/fine-tuning) + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual async Task GetJobAsync(RequestOptions? options) + { + using PipelineMessage message = CreateRetrieveFineTuningJobRequest(_jobId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Get info about a fine-tuning job. + /// + /// [Learn more about fine-tuning](/docs/guides/fine-tuning) + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual ClientResult GetJob(RequestOptions? options) + { + using PipelineMessage message = CreateRetrieveFineTuningJobRequest(_jobId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Immediately cancel a fine-tune job. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual async Task CancelAsync(RequestOptions? options) + { + using PipelineMessage message = CreateCancelFineTuningJobRequest(_jobId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Immediately cancel a fine-tune job. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual ClientResult Cancel(RequestOptions? options) + { + using PipelineMessage message = CreateCancelFineTuningJobRequest(_jobId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Get status updates for a fine-tuning job. + /// + /// Identifier for the last event from the previous pagination request. + /// Number of events to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual AsyncCollectionResult GetJobEventsAsync(string? after, int? limit, RequestOptions options) + { + return new AsyncFineTuningJobEventCollectionResult(this, options, limit, after); + } + + // CUSTOM: + // - Renamed. + // - Edited doc comment. + /// + /// [Protocol Method] Get status updates for a fine-tuning job. + /// + /// Identifier for the last event from the previous pagination request. + /// Number of events to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual CollectionResult GetJobEvents( string? after, int? limit, RequestOptions options) + { + return new FineTuningJobEventCollectionResult(this, options, limit, after); + } + + /// + /// [Protocol Method] List the checkpoints for a fine-tuning job. + /// + /// Identifier for the last checkpoint ID from the previous pagination request. + /// Number of checkpoints to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual AsyncCollectionResult GetJobCheckpointsAsync(string? after, int? limit, RequestOptions? options) + { + return new AsyncFineTuningJobCheckpointCollectionResult(this, options, limit, after); + } + + /// + /// [Protocol Method] List the checkpoints for a fine-tuning job. + /// + /// Identifier for the last checkpoint ID from the previous pagination request. + /// Number of checkpoints to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + public virtual CollectionResult GetJobCheckpoints(string? after, int? limit, RequestOptions? options) + { + return new FineTuningJobCheckpointCollectionResult(this, options, limit, after); + } + + /// + /// [Protocol Method] List the checkpoints for a fine-tuning job. + /// + /// Identifier for the last checkpoint ID from the previous pagination request. + /// Number of checkpoints to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task GetJobCheckpointsPageAsync(string? after, int? limit, RequestOptions? options) + { + using PipelineMessage message = CreateGetFineTuningJobCheckpointsRequest(_jobId, after, limit, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] List the checkpoints for a fine-tuning job. + /// + /// Identifier for the last checkpoint ID from the previous pagination request. + /// Number of checkpoints to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual ClientResult GetJobPageCheckpoints(string? after, int? limit, RequestOptions? options) + { + using PipelineMessage message = CreateGetFineTuningJobCheckpointsRequest(_jobId, after, limit, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + /// + /// [Protocol Method] List the checkpoints for a fine-tuning job. + /// + /// Identifier for the last checkpoint ID from the previous pagination request. + /// Number of checkpoints to retrieve. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual ClientResult GetJobCheckpointsPage(string? after, int? limit, RequestOptions? options) + { + using PipelineMessage message = CreateGetFineTuningJobCheckpointsRequest(_jobId, after, limit, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + internal virtual async Task GetJobEventsPageAsync(string? after, int? limit, RequestOptions? options) + { + using PipelineMessage message = CreateGetFineTuningEventsRequest(_jobId, after, limit, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + internal virtual ClientResult GetJobEventsPage(string? after, int? limit, RequestOptions? options) + { + using PipelineMessage message = CreateGetFineTuningEventsRequest(_jobId, after, limit, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + + } + + internal virtual PipelineMessage CreateRetrieveFineTuningJobRequest(string fineTuningJobId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs/", false); + uri.AppendPath(fineTuningJobId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateCancelFineTuningJobRequest(string fineTuningJobId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs/", false); + uri.AppendPath(fineTuningJobId, true); + uri.AppendPath("/cancel", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateGetFineTuningJobCheckpointsRequest(string fineTuningJobId, string? after, int? limit, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs/", false); + uri.AppendPath(fineTuningJobId, true); + uri.AppendPath("/checkpoints", false); + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateGetFineTuningEventsRequest(string jobId, string? after, int? limit, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/fine_tuning/jobs/", false); + uri.AppendPath(jobId, true); + uri.AppendPath("/events", false); + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} diff --git a/src/Custom/FineTuning/Internal/FineTuningJobOperationToken.cs b/src/Custom/FineTuning/Internal/FineTuningJobOperationToken.cs new file mode 100644 index 00000000..915a1112 --- /dev/null +++ b/src/Custom/FineTuning/Internal/FineTuningJobOperationToken.cs @@ -0,0 +1,91 @@ +using System; +using System.ClientModel; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +#nullable enable + +namespace OpenAI.FineTuning; + +internal class FineTuningJobOperationToken : ContinuationToken +{ + public FineTuningJobOperationToken(string jobId) + { + JobId = jobId; + } + + public string JobId { get; } + + public override BinaryData ToBytes() + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + + writer.WriteStartObject(); + + writer.WriteString("jobId", JobId); + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); + } + + public static FineTuningJobOperationToken FromToken(ContinuationToken continuationToken) + { + if (continuationToken is FineTuningJobOperationToken token) + { + return token; + } + + BinaryData data = continuationToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create FineTuningJobOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + Utf8JsonReader reader = new(data); + + string jobId = null!; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "jobId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + jobId = reader.GetString()!; + break; + + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + if (jobId is null) + { + throw new ArgumentException("Failed to create FineTuningJobOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + return new(jobId); + } +} + diff --git a/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobCheckpointCollectionResult.cs b/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobCheckpointCollectionResult.cs index 97c14485..2e027835 100644 --- a/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobCheckpointCollectionResult.cs +++ b/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobCheckpointCollectionResult.cs @@ -11,24 +11,21 @@ namespace OpenAI.FineTuning; internal class AsyncFineTuningJobCheckpointCollectionResult : AsyncCollectionResult { - private readonly FineTuningClient _fineTuningClient; - private readonly ClientPipeline _pipeline; + private readonly FineTuningJobOperation _operation; private readonly RequestOptions? _options; // Initial values - private readonly string _jobId; private readonly int? _limit; - private readonly string _after; + private readonly string? _after; - public AsyncFineTuningJobCheckpointCollectionResult(FineTuningClient fineTuningClient, - ClientPipeline pipeline, RequestOptions? options, - string jobId, int? limit, string after) + public AsyncFineTuningJobCheckpointCollectionResult( + FineTuningJobOperation fineTuningJobOperation, + RequestOptions? options, + int? limit, string? after) { - _fineTuningClient = fineTuningClient; - _pipeline = pipeline; + _operation = fineTuningJobOperation; _options = options; - _jobId = jobId; _limit = limit; _after = after; } @@ -49,11 +46,11 @@ public async override IAsyncEnumerable GetRawPagesAsync() { Argument.AssertNotNull(page, nameof(page)); - return FineTuningJobEventCollectionPageToken.FromResponse(page, _jobId, _limit); + return FineTuningJobEventCollectionPageToken.FromResponse(page, _operation.JobId, _limit); } public async Task GetFirstPageAsync() - => await GetJobCheckpointsAsync(_jobId, _after, _limit, _options).ConfigureAwait(false); + => await _operation.GetJobCheckpointsPageAsync(_after, _limit, _options).ConfigureAwait(false); public async Task GetNextPageAsync(ClientResult result) { @@ -68,17 +65,9 @@ public async Task GetNextPageAsync(ClientResult result) string? lastId = lastItem.TryGetProperty("id", out JsonElement idElement) ? idElement.GetString() : null; - return await GetJobCheckpointsAsync(_jobId, lastId, _limit, _options).ConfigureAwait(false); + return await _operation.GetJobCheckpointsPageAsync(lastId, _limit, _options).ConfigureAwait(false); } public static bool HasNextPage(ClientResult result) => FineTuningJobCheckpointCollectionResult.HasNextPage(result); - - internal virtual async Task GetJobCheckpointsAsync(string jobId, string? after, int? limit, RequestOptions? options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = _fineTuningClient.CreateGetFineTuningJobCheckpointsRequest(jobId, after, limit, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); - } } diff --git a/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobEventCollectionResult.cs b/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobEventCollectionResult.cs index c84d4bca..573dcec2 100644 --- a/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobEventCollectionResult.cs +++ b/src/Custom/FineTuning/Internal/Pagination/AsyncFineTuningJobEventCollectionResult.cs @@ -11,24 +11,21 @@ namespace OpenAI.FineTuning; internal class AsyncFineTuningJobEventCollectionResult : AsyncCollectionResult { - private readonly FineTuningClient _fineTuningClient; - private readonly ClientPipeline _pipeline; + private readonly FineTuningJobOperation _operation; private readonly RequestOptions? _options; // Initial values - private readonly string _jobId; private readonly int? _limit; - private readonly string _after; + private readonly string? _after; - public AsyncFineTuningJobEventCollectionResult(FineTuningClient fineTuningClient, - ClientPipeline pipeline, RequestOptions? options, - string jobId, int? limit, string after) + public AsyncFineTuningJobEventCollectionResult( + FineTuningJobOperation fineTuningJobOperation, + RequestOptions? options, + int? limit, string? after) { - _fineTuningClient = fineTuningClient; - _pipeline = pipeline; + _operation = fineTuningJobOperation; _options = options; - _jobId = jobId; _limit = limit; _after = after; } @@ -49,11 +46,11 @@ public async override IAsyncEnumerable GetRawPagesAsync() { Argument.AssertNotNull(page, nameof(page)); - return FineTuningJobEventCollectionPageToken.FromResponse(page, _jobId, _limit); + return FineTuningJobEventCollectionPageToken.FromResponse(page, _operation.JobId, _limit); } public async Task GetFirstPageAsync() - => await GetJobEventsAsync(_jobId, _after, _limit, _options).ConfigureAwait(false); + => await _operation.GetJobEventsPageAsync(_after, _limit, _options).ConfigureAwait(false); public async Task GetNextPageAsync(ClientResult result) { @@ -68,18 +65,9 @@ public async Task GetNextPageAsync(ClientResult result) string? lastId = lastItem.TryGetProperty("id", out JsonElement idElement) ? idElement.GetString() : null; - return await GetJobEventsAsync(_jobId, lastId, _limit, _options).ConfigureAwait(false); + return await _operation.GetJobEventsPageAsync(lastId, _limit, _options).ConfigureAwait(false); } public static bool HasNextPage(ClientResult result) => FineTuningJobEventCollectionResult.HasNextPage(result); - - - internal virtual async Task GetJobEventsAsync(string jobId, string? after, int? limit, RequestOptions? options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = _fineTuningClient.CreateGetFineTuningEventsRequest(jobId, after, limit, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); - } } diff --git a/src/Custom/FineTuning/Internal/Pagination/FineTuningJobCheckpointCollectionResult.cs b/src/Custom/FineTuning/Internal/Pagination/FineTuningJobCheckpointCollectionResult.cs index e884fcbf..815829d2 100644 --- a/src/Custom/FineTuning/Internal/Pagination/FineTuningJobCheckpointCollectionResult.cs +++ b/src/Custom/FineTuning/Internal/Pagination/FineTuningJobCheckpointCollectionResult.cs @@ -10,24 +10,21 @@ namespace OpenAI.FineTuning; internal class FineTuningJobCheckpointCollectionResult : CollectionResult { - private readonly FineTuningClient _fineTuningClient; - private readonly ClientPipeline _pipeline; + private readonly FineTuningJobOperation _operation; private readonly RequestOptions? _options; // Initial values - private readonly string _jobId; private readonly int? _limit; - private readonly string _after; + private readonly string? _after; - public FineTuningJobCheckpointCollectionResult(FineTuningClient fineTuningClient, - ClientPipeline pipeline, RequestOptions? options, - string jobId, int? limit, string after) + public FineTuningJobCheckpointCollectionResult( + FineTuningJobOperation fineTuningJobOperation, + RequestOptions? options, + int? limit, string? after) { - _fineTuningClient = fineTuningClient; - _pipeline = pipeline; + _operation = fineTuningJobOperation; _options = options; - _jobId = jobId; _limit = limit; _after = after; } @@ -48,11 +45,11 @@ public override IEnumerable GetRawPages() { Argument.AssertNotNull(page, nameof(page)); - return FineTuningJobCheckpointCollectionPageToken.FromResponse(page, _jobId, _limit); + return FineTuningJobCheckpointCollectionPageToken.FromResponse(page, _operation.JobId, _limit); } public ClientResult GetFirstPage() - => GetJobCheckpoints(_jobId, _after, _limit, _options); + => _operation.GetJobCheckpointsPage(_after, _limit, _options); public ClientResult GetNextPage(ClientResult result) { @@ -67,7 +64,7 @@ public ClientResult GetNextPage(ClientResult result) string? lastId = lastItem.TryGetProperty("id", out JsonElement idElement) ? idElement.GetString() : null; - return GetJobCheckpoints(_jobId, lastId, _limit, _options); + return _operation.GetJobCheckpointsPage(lastId, _limit, _options); } public static bool HasNextPage(ClientResult result) @@ -81,11 +78,4 @@ public static bool HasNextPage(ClientResult result) return hasMore; } - internal virtual ClientResult GetJobCheckpoints(string jobId, string? after, int? limit, RequestOptions? options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = _fineTuningClient.CreateGetFineTuningJobCheckpointsRequest(jobId, after, limit, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); - } } diff --git a/src/Custom/FineTuning/Internal/Pagination/FineTuningJobEventCollectionResult.cs b/src/Custom/FineTuning/Internal/Pagination/FineTuningJobEventCollectionResult.cs index 5215dffc..06c56d9a 100644 --- a/src/Custom/FineTuning/Internal/Pagination/FineTuningJobEventCollectionResult.cs +++ b/src/Custom/FineTuning/Internal/Pagination/FineTuningJobEventCollectionResult.cs @@ -10,24 +10,21 @@ namespace OpenAI.FineTuning; internal class FineTuningJobEventCollectionResult : CollectionResult { - private readonly FineTuningClient _fineTuningClient; - private readonly ClientPipeline _pipeline; + private readonly FineTuningJobOperation _operation; private readonly RequestOptions? _options; // Initial values - private readonly string _jobId; private readonly int? _limit; - private readonly string _after; + private readonly string? _after; - public FineTuningJobEventCollectionResult(FineTuningClient fineTuningClient, - ClientPipeline pipeline, RequestOptions? options, - string jobId, int? limit, string after) + public FineTuningJobEventCollectionResult( + FineTuningJobOperation fineTuningJobOperation, + RequestOptions? options, + int? limit, string? after) { - _fineTuningClient = fineTuningClient; - _pipeline = pipeline; + _operation = fineTuningJobOperation; _options = options; - _jobId = jobId; _limit = limit; _after = after; } @@ -48,11 +45,11 @@ public override IEnumerable GetRawPages() { Argument.AssertNotNull(page, nameof(page)); - return FineTuningJobEventCollectionPageToken.FromResponse(page, _jobId, _limit); + return FineTuningJobEventCollectionPageToken.FromResponse(page, _operation.JobId, _limit); } public ClientResult GetFirstPage() - => GetJobEvents(_jobId, _after, _limit, _options); + => _operation.GetJobEventsPage(_after, _limit, _options); public ClientResult GetNextPage(ClientResult result) { @@ -67,7 +64,7 @@ public ClientResult GetNextPage(ClientResult result) string? lastId = lastItem.TryGetProperty("id", out JsonElement idElement) ? idElement.GetString() : null; - return GetJobEvents(_jobId, lastId, _limit, _options); + return _operation.GetJobEventsPage(lastId, _limit, _options); } public static bool HasNextPage(ClientResult result) @@ -81,12 +78,4 @@ public static bool HasNextPage(ClientResult result) return hasMore; } - - internal virtual ClientResult GetJobEvents(string jobId, string? after, int? limit, RequestOptions? options) - { - Argument.AssertNotNullOrEmpty(jobId, nameof(jobId)); - - using PipelineMessage message = _fineTuningClient.CreateGetFineTuningEventsRequest(jobId, after, limit, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); - } } diff --git a/src/Custom/Images/GeneratedImageFormat.cs b/src/Custom/Images/GeneratedImageFormat.cs index 93bdca9e..79984385 100644 --- a/src/Custom/Images/GeneratedImageFormat.cs +++ b/src/Custom/Images/GeneratedImageFormat.cs @@ -1,37 +1,20 @@ namespace OpenAI.Images; -// CUSTOM: -// - Renamed enum and its members. -// - Converted extensible enum into an enum. -// - Edited doc comment. -/// -/// Represents the available output methods for generated images. -/// -/// -/// url - - Default, provides a temporary internet location that -/// the generated image can be retrieved from. -/// -/// -/// b64_json - - Provides the full image data on the response, -/// encoded in the result as a base64 string. This offers the fastest round trip time but can drastically -/// increase the size of response payloads. -/// -/// -/// +// CUSTOM: Renamed. +/// The format in which the generated images are returned by the service. [CodeGenModel("CreateImageRequestResponseFormat")] -public enum GeneratedImageFormat +public readonly partial struct GeneratedImageFormat { - /// - /// Instructs the request to return image data directly on the response, encoded as a base64 string in the response - /// JSON. This minimizes availability time but drastically increases the size of responses, required bandwidth, and - /// immediate memory needs. This is equivalent to b64_json in the REST API. - /// + // CUSTOM: Renamed. + /// Returned as bytes in a base64-encoded string. [CodeGenMember("B64Json")] - Bytes, + public static GeneratedImageFormat Bytes { get; } = new GeneratedImageFormat(BytesValue); + + // CUSTOM: Renamed. /// - /// The default setting that instructs the request to return a temporary internet location from which the image can - /// be retrieved. + /// Returned as a URI pointing to a temporary internet location from where the image can be downlaoded. This URI is + /// only valid for 60 minutes after the image is generated. /// [CodeGenMember("Url")] - Uri, + public static GeneratedImageFormat Uri { get; } = new GeneratedImageFormat(UriValue); } \ No newline at end of file diff --git a/src/Custom/Images/GeneratedImageQuality.cs b/src/Custom/Images/GeneratedImageQuality.cs index 6f25f99e..df4a59e0 100644 --- a/src/Custom/Images/GeneratedImageQuality.cs +++ b/src/Custom/Images/GeneratedImageQuality.cs @@ -1,31 +1,13 @@ namespace OpenAI.Images; -// CUSTOM: -// - Renamed enum and its members. -// - Converted extensible enum into an enum. -// - Edited doc comment. -/// -/// A representation of the quality setting for image operations that controls the level of work that the model will -/// perform. +// CUSTOM: Renamed. +/// +/// The quality of the image that will be generated. creates images with finer details and +/// greater consistency across the image. /// -/// -/// Available qualities consist of: -/// -/// - standard - The default setting that balances speed, detail, and consistecy. -/// - hd - Better consistency and finer details, but may be slower. -/// -/// [CodeGenModel("CreateImageRequestQuality")] -public enum GeneratedImageQuality +public readonly partial struct GeneratedImageQuality { - /// - /// The hd image quality that provides finer details and greater consistency but may be slower. - /// [CodeGenMember("Hd")] - High, - /// - /// The standard image quality that provides a balanced mix of detailing, consistency, and speed. - /// - [CodeGenMember("Standard")] - Standard, + public static GeneratedImageQuality High { get; } = new GeneratedImageQuality(HighValue); } \ No newline at end of file diff --git a/src/Custom/Images/GeneratedImageSize.cs b/src/Custom/Images/GeneratedImageSize.cs index 49acb361..8b16ccc3 100644 --- a/src/Custom/Images/GeneratedImageSize.cs +++ b/src/Custom/Images/GeneratedImageSize.cs @@ -3,9 +3,7 @@ namespace OpenAI.Images; // CUSTOM: Added custom struct in favor of the generated extensible enum. -/// -/// Represents the available output dimensions for generated images. -/// +/// The size of the image that will be generated. [CodeGenModel("CreateImageRequestSize")] [CodeGenSuppress("GeneratedImageSize", typeof(string))] [CodeGenSuppress("op_Implicit", typeof(string))] diff --git a/src/Custom/Images/GeneratedImageStyle.cs b/src/Custom/Images/GeneratedImageStyle.cs index 1ea6c49d..60a53c95 100644 --- a/src/Custom/Images/GeneratedImageStyle.cs +++ b/src/Custom/Images/GeneratedImageStyle.cs @@ -1,23 +1,12 @@ namespace OpenAI.Images; -// CUSTOM: -// - Renamed. -// - Converted extensible enum into an enum. -// - Edited doc comment. +// CUSTOM: Renamed. /// -/// The style of the generated images. Must be one of vivid or natural. Vivid causes the model to lean towards -/// generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real -/// looking images. This param is only supported for dall-e-3. +/// The style of the image that will be generated. causes the model to lean towards generating +/// hyper-real and dramatic images. causes the model to produce more natural, less hyper-real +/// looking images. /// [CodeGenModel("CreateImageRequestStyle")] -public enum GeneratedImageStyle +public readonly partial struct GeneratedImageStyle { - /// - /// The vivid style, with which the model will tend towards hyper-realistic, dramatic imagery. - /// - Vivid, - /// - /// The natural style, with which the model will not tend towards hyper-realistic, dramatic imagery. - /// - Natural, } \ No newline at end of file diff --git a/src/Custom/Images/ImageClient.cs b/src/Custom/Images/ImageClient.cs index 9077805e..297f8951 100644 --- a/src/Custom/Images/ImageClient.cs +++ b/src/Custom/Images/ImageClient.cs @@ -41,17 +41,6 @@ public partial class ImageClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public ImageClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. diff --git a/src/Custom/Images/ImageEditOptions.cs b/src/Custom/Images/ImageEditOptions.cs index 47f83609..62126899 100644 --- a/src/Custom/Images/ImageEditOptions.cs +++ b/src/Custom/Images/ImageEditOptions.cs @@ -111,14 +111,7 @@ internal MultipartFormDataBinaryContent ToMultipartContent(Stream image, string if (ResponseFormat is not null) { - string format = ResponseFormat switch - { - GeneratedImageFormat.Uri => "url", - GeneratedImageFormat.Bytes => "b64_json", - _ => throw new ArgumentException(nameof(ResponseFormat)), - }; - - content.Add(format, "response_format"); + content.Add(ResponseFormat.ToString(), "response_format"); } if (Size is not null) diff --git a/src/Custom/Images/ImageVariationOptions.cs b/src/Custom/Images/ImageVariationOptions.cs index 0f71b265..43d6269e 100644 --- a/src/Custom/Images/ImageVariationOptions.cs +++ b/src/Custom/Images/ImageVariationOptions.cs @@ -78,14 +78,7 @@ internal MultipartFormDataBinaryContent ToMultipartContent(Stream image, string if (ResponseFormat is not null) { - string format = ResponseFormat switch - { - GeneratedImageFormat.Uri => "url", - GeneratedImageFormat.Bytes => "b64_json", - _ => throw new ArgumentException(nameof(ResponseFormat)), - }; - - content.Add(format, "response_format"); + content.Add(ResponseFormat.ToString(), "response_format"); } if (Size is not null) diff --git a/src/Custom/LegacyCompletions/Internal/LegacyCompletionClient.cs b/src/Custom/LegacyCompletions/Internal/LegacyCompletionClient.cs index 53103aaa..09b149c4 100644 --- a/src/Custom/LegacyCompletions/Internal/LegacyCompletionClient.cs +++ b/src/Custom/LegacyCompletions/Internal/LegacyCompletionClient.cs @@ -31,17 +31,6 @@ internal partial class LegacyCompletionClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public LegacyCompletionClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. diff --git a/src/Custom/Models/ModelClient.cs b/src/Custom/Models/ModelClient.cs index 44079d90..26bd4d7d 100644 --- a/src/Custom/Models/ModelClient.cs +++ b/src/Custom/Models/ModelClient.cs @@ -36,15 +36,6 @@ public partial class ModelClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public ModelClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. diff --git a/src/Custom/Moderations/ModerationClient.cs b/src/Custom/Moderations/ModerationClient.cs index aeb7a2b1..d3912b28 100644 --- a/src/Custom/Moderations/ModerationClient.cs +++ b/src/Custom/Moderations/ModerationClient.cs @@ -37,17 +37,6 @@ public partial class ModerationClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The name of the model to use in requests sent to the service. To learn more about the available models, see . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// or is null. - /// is an empty string, and was expected to be non-empty. - public ModerationClient(string model, string apiKey, OpenAIClientOptions options) : this(model, new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Added `model` parameter. // - Used a custom pipeline. diff --git a/src/Custom/OpenAIClient.cs b/src/Custom/OpenAIClient.cs index f145cd54..18add1da 100644 --- a/src/Custom/OpenAIClient.cs +++ b/src/Custom/OpenAIClient.cs @@ -88,15 +88,6 @@ private static class KnownHeaderNames { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public OpenAIClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. diff --git a/src/Custom/VectorStores/AddFileToVectorStoreOperation.Protocol.cs b/src/Custom/VectorStores/AddFileToVectorStoreOperation.Protocol.cs new file mode 100644 index 00000000..254232f1 --- /dev/null +++ b/src/Custom/VectorStores/AddFileToVectorStoreOperation.Protocol.cs @@ -0,0 +1,68 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.ComponentModel; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +public partial class AddFileToVectorStoreOperation : OperationResult +{ + private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; + + private readonly string _vectorStoreId; + private readonly string _fileId; + + /// + public override ContinuationToken? RehydrationToken { get; protected set; } + + /// + /// [Protocol Method] Retrieves a vector store file. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task GetFileAssociationAsync(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreFileRequest(_vectorStoreId, _fileId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Retrieves a vector store file. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetFileAssociation(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreFileRequest(_vectorStoreId, _fileId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + internal virtual PipelineMessage CreateGetVectorStoreFileRequest(string vectorStoreId, string fileId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/files/", false); + uri.AppendPath(fileId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} diff --git a/src/Custom/VectorStores/AddFileToVectorStoreOperation.cs b/src/Custom/VectorStores/AddFileToVectorStoreOperation.cs new file mode 100644 index 00000000..c0004a4d --- /dev/null +++ b/src/Custom/VectorStores/AddFileToVectorStoreOperation.cs @@ -0,0 +1,187 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +[Experimental("OPENAI001")] +public partial class AddFileToVectorStoreOperation : OperationResult +{ + internal AddFileToVectorStoreOperation( + ClientPipeline pipeline, + Uri endpoint, + ClientResult result) + : base(result.GetRawResponse()) + { + _pipeline = pipeline; + _endpoint = endpoint; + + Value = result; + Status = Value.Status; + + _vectorStoreId = Value.VectorStoreId; + _fileId = Value.FileId; + + HasCompleted = GetHasCompleted(Value.Status); + RehydrationToken = new AddFileToVectorStoreOperationToken(VectorStoreId, FileId); + } + + /// + /// The current value of the add file to vector store operation in progress. + /// + public VectorStoreFileAssociation? Value { get; private set; } + + /// + /// The current status of the add file to vector store operation in progress. + /// + public VectorStoreFileAssociationStatus? Status { get; private set; } + + /// + /// The ID of the vector store the file is being added to. + /// + public string VectorStoreId { get => _vectorStoreId; } + + /// + /// The ID of the file being added to the vector store. + /// + public string FileId { get => _fileId; } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + AddFileToVectorStoreOperationToken token = AddFileToVectorStoreOperationToken.FromToken(rehydrationToken); + + ClientResult result = await client.GetFileAssociationAsync(token.VectorStoreId, token.FileId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + return client.CreateAddFileToVectorStoreOperation(ClientResult.FromValue(value, response)); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static AddFileToVectorStoreOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + AddFileToVectorStoreOperationToken token = AddFileToVectorStoreOperationToken.FromToken(rehydrationToken); + + ClientResult result = client.GetFileAssociation(token.VectorStoreId, token.FileId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + return client.CreateAddFileToVectorStoreOperation(ClientResult.FromValue(value, response)); + } + + /// + public override async ValueTask UpdateStatusAsync(RequestOptions? options = null) + { + ClientResult result = await GetFileAssociationAsync(options).ConfigureAwait(false); + + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + /// + public override ClientResult UpdateStatus(RequestOptions? options = null) + { + ClientResult result = GetFileAssociation(options); + + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + internal async Task WaitUntilAsync(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + await WaitForCompletionAsync(options?.CancellationToken ?? default).ConfigureAwait(false); + return this; + } + + internal AddFileToVectorStoreOperation WaitUntil(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + WaitForCompletion(options?.CancellationToken ?? default); + return this; + } + + private void ApplyUpdate(PipelineResponse response, VectorStoreFileAssociation value) + { + Value = value; + Status = value.Status; + + HasCompleted = GetHasCompleted(value.Status); + SetRawResponse(response); + } + + private static bool GetHasCompleted(VectorStoreFileAssociationStatus status) + { + return status == VectorStoreFileAssociationStatus.Completed || + status == VectorStoreFileAssociationStatus.Cancelled || + status == VectorStoreFileAssociationStatus.Failed; + } + + /// + /// Gets a instance representing an existing association between a known + /// vector store ID and file ID. + /// + /// A token that can be used to cancel this method call. + /// A instance. + public virtual async Task> GetFileAssociationAsync(CancellationToken cancellationToken = default) + { + ClientResult result = await GetFileAssociationAsync(cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Gets a instance representing an existing association between a known + /// vector store ID and file ID. + /// + /// A token that can be used to cancel this method call. + /// A instance. + public virtual ClientResult GetFileAssociation(CancellationToken cancellationToken = default) + { + ClientResult result = GetFileAssociation(cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + return ClientResult.FromValue(value, response); + } +} diff --git a/src/Custom/VectorStores/CreateBatchFileJobOperation.Protocol.cs b/src/Custom/VectorStores/CreateBatchFileJobOperation.Protocol.cs new file mode 100644 index 00000000..8b649718 --- /dev/null +++ b/src/Custom/VectorStores/CreateBatchFileJobOperation.Protocol.cs @@ -0,0 +1,119 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +/// +/// Long-running operation for creating a vector store file batch. +/// +public partial class CreateBatchFileJobOperation : OperationResult +{ + private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; + + private readonly string _vectorStoreId; + private readonly string _batchId; + + /// + public override ContinuationToken? RehydrationToken { get; protected set; } + + // Generated protocol methods + + /// + /// [Protocol Method] Retrieves a vector store file batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task GetFileBatchAsync(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreFileBatchRequest(_vectorStoreId, _batchId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Retrieves a vector store file batch. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetFileBatch(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreFileBatchRequest(_vectorStoreId, _batchId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + /// + /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task CancelAsync(RequestOptions? options) + { + using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(_vectorStoreId, _batchId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult Cancel(RequestOptions? options) + { + using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(_vectorStoreId, _batchId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + internal virtual PipelineMessage CreateGetVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches/", false); + uri.AppendPath(batchId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateCancelVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches/", false); + uri.AppendPath(batchId, true); + uri.AppendPath("/cancel", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} \ No newline at end of file diff --git a/src/Custom/VectorStores/CreateBatchFileJobOperation.cs b/src/Custom/VectorStores/CreateBatchFileJobOperation.cs new file mode 100644 index 00000000..59511d9f --- /dev/null +++ b/src/Custom/VectorStores/CreateBatchFileJobOperation.cs @@ -0,0 +1,216 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +/// +/// Long-running operation for creating a vector store file batch. +/// +[Experimental("OPENAI001")] +public partial class CreateBatchFileJobOperation : OperationResult +{ + internal CreateBatchFileJobOperation( + ClientPipeline pipeline, + Uri endpoint, + ClientResult result) + : base(result.GetRawResponse()) + { + _pipeline = pipeline; + _endpoint = endpoint; + + Value = result; + Status = Value.Status; + + _vectorStoreId = Value.VectorStoreId; + _batchId = Value.BatchId; + + HasCompleted = GetHasCompleted(Value.Status); + RehydrationToken = new CreateBatchFileJobOperationToken(VectorStoreId, BatchId); + } + + /// + /// The current value of the in progress. + /// + public VectorStoreBatchFileJob? Value { get; private set; } + + /// + /// The current status of the in progress. + /// + public VectorStoreBatchFileJobStatus? Status { get; private set; } + + /// + /// The ID of the vector store corresponding to this batch file operation. + /// + public string VectorStoreId { get => _vectorStoreId; } + + /// + /// The ID of the batch file job represented by this operation. + /// + public string BatchId { get => _batchId; } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateBatchFileJobOperationToken token = CreateBatchFileJobOperationToken.FromToken(rehydrationToken); + + ClientResult result = await client.GetBatchFileJobAsync(token.VectorStoreId, token.BatchId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob job = VectorStoreBatchFileJob.FromResponse(response); + + return client.CreateBatchFileJobOperation(ClientResult.FromValue(job, response)); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static CreateBatchFileJobOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateBatchFileJobOperationToken token = CreateBatchFileJobOperationToken.FromToken(rehydrationToken); + + ClientResult result = client.GetBatchFileJob(token.VectorStoreId, token.BatchId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob job = VectorStoreBatchFileJob.FromResponse(response); + + return client.CreateBatchFileJobOperation(ClientResult.FromValue(job, response)); + } + + /// + public override async ValueTask UpdateStatusAsync(RequestOptions? options = null) + { + ClientResult result = await GetFileBatchAsync(options).ConfigureAwait(false); + + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + /// + public override ClientResult UpdateStatus(RequestOptions? options = null) + { + ClientResult result = GetFileBatch(options); + + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + internal async Task WaitUntilAsync(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + await WaitForCompletionAsync(options?.CancellationToken ?? default).ConfigureAwait(false); + return this; + } + + internal CreateBatchFileJobOperation WaitUntil(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + WaitForCompletion(options?.CancellationToken ?? default); + return this; + } + + private void ApplyUpdate(PipelineResponse response, VectorStoreBatchFileJob value) + { + Value = value; + Status = value.Status; + + HasCompleted = GetHasCompleted(value.Status); + SetRawResponse(response); + } + + private static bool GetHasCompleted(VectorStoreBatchFileJobStatus status) + { + return status == VectorStoreBatchFileJobStatus.Completed || + status == VectorStoreBatchFileJobStatus.Cancelled || + status == VectorStoreBatchFileJobStatus.Failed; + } + + // Generated convenience methods + + /// + /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. + /// + /// A token that can be used to cancel this method call. + /// A instance representing the ingestion operation. + public virtual async Task> GetFileBatchAsync(CancellationToken cancellationToken = default) + { + ClientResult result = await GetFileBatchAsync(cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. + /// + /// A token that can be used to cancel this method call. + /// A instance representing the ingestion operation. + public virtual ClientResult GetFileBatch(CancellationToken cancellationToken = default) + { + ClientResult result = GetFileBatch(cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Cancels an in-progress . + /// + /// A token that can be used to cancel this method call. + /// An updated instance. + public virtual async Task> CancelAsync(CancellationToken cancellationToken = default) + { + ClientResult result = await CancelAsync(cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Cancels an in-progress . + /// + /// A token that can be used to cancel this method call. + /// An updated instance. + public virtual ClientResult Cancel(CancellationToken cancellationToken = default) + { + ClientResult result = Cancel(cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } +} \ No newline at end of file diff --git a/src/Custom/VectorStores/CreateVectorStoreOperation.Protocol.cs b/src/Custom/VectorStores/CreateVectorStoreOperation.Protocol.cs new file mode 100644 index 00000000..343c3715 --- /dev/null +++ b/src/Custom/VectorStores/CreateVectorStoreOperation.Protocol.cs @@ -0,0 +1,65 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.ComponentModel; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +public partial class CreateVectorStoreOperation : OperationResult +{ + private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; + + private readonly string _vectorStoreId; + + /// + public override ContinuationToken? RehydrationToken { get; protected set; } + + /// + /// [Protocol Method] Retrieves a vector store. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task GetVectorStoreAsync(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreRequest(_vectorStoreId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Retrieves a vector store. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult GetVectorStore(RequestOptions? options) + { + using PipelineMessage message = CreateGetVectorStoreRequest(_vectorStoreId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + internal virtual PipelineMessage CreateGetVectorStoreRequest(string vectorStoreId, RequestOptions? options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} diff --git a/src/Custom/VectorStores/CreateVectorStoreOperation.cs b/src/Custom/VectorStores/CreateVectorStoreOperation.cs new file mode 100644 index 00000000..1171fd07 --- /dev/null +++ b/src/Custom/VectorStores/CreateVectorStoreOperation.cs @@ -0,0 +1,177 @@ +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenAI.VectorStores; + +[Experimental("OPENAI001")] +public partial class CreateVectorStoreOperation : OperationResult +{ + internal CreateVectorStoreOperation( + ClientPipeline pipeline, + Uri endpoint, + ClientResult result) + : base(result.GetRawResponse()) + { + _pipeline = pipeline; + _endpoint = endpoint; + + Value = result; + Status = Value.Status; + + _vectorStoreId = Value.Id; + + HasCompleted = GetHasCompleted(Value.Status); + RehydrationToken = new CreateVectorStoreOperationToken(VectorStoreId); + } + + /// + /// The current value of the create operation in progress. + /// + public VectorStore? Value { get; private set; } + + /// + /// The current status of the create operation in progress. + /// + public VectorStoreStatus? Status { get; private set; } + + /// + /// The ID of the vector store being created. + /// + public string VectorStoreId { get => _vectorStoreId; } + + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static async Task RehydrateAsync(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateVectorStoreOperationToken token = CreateVectorStoreOperationToken.FromToken(rehydrationToken); + + ClientResult result = await client.GetVectorStoreAsync(token.VectorStoreId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result.GetRawResponse(); + VectorStore vectorStore = VectorStore.FromResponse(response); + + return client.CreateCreateVectorStoreOperation(ClientResult.FromValue(vectorStore, response)); + } + + /// + /// Recreates a from a rehydration token. + /// + /// The used to obtain the + /// operation status from the service. + /// The rehydration token corresponding to + /// the operation to rehydrate. + /// A token that can be used to cancel the + /// request. + /// The rehydrated operation. + /// or is null. + public static CreateVectorStoreOperation Rehydrate(VectorStoreClient client, ContinuationToken rehydrationToken, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(client, nameof(client)); + Argument.AssertNotNull(rehydrationToken, nameof(rehydrationToken)); + + CreateVectorStoreOperationToken token = CreateVectorStoreOperationToken.FromToken(rehydrationToken); + + ClientResult result = client.GetVectorStore(token.VectorStoreId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result.GetRawResponse(); + VectorStore vectorStore = VectorStore.FromResponse(response); + + return client.CreateCreateVectorStoreOperation(ClientResult.FromValue(vectorStore, response)); + } + + /// + public override async ValueTask UpdateStatusAsync(RequestOptions? options = null) + { + ClientResult result = await GetVectorStoreAsync(options).ConfigureAwait(false); + + PipelineResponse response = result.GetRawResponse(); + VectorStore value = VectorStore.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + /// + public override ClientResult UpdateStatus(RequestOptions? options = null) + { + ClientResult result = GetVectorStore(options); + + PipelineResponse response = result.GetRawResponse(); + VectorStore value = VectorStore.FromResponse(response); + + ApplyUpdate(response, value); + + return result; + } + + internal async Task WaitUntilAsync(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + await WaitForCompletionAsync(options?.CancellationToken ?? default).ConfigureAwait(false); + return this; + } + + internal CreateVectorStoreOperation WaitUntil(bool waitUntilCompleted, RequestOptions? options) + { + if (!waitUntilCompleted) return this; + WaitForCompletion(options?.CancellationToken ?? default); + return this; + } + + private void ApplyUpdate(PipelineResponse response, VectorStore value) + { + Value = value; + Status = value.Status; + + HasCompleted = GetHasCompleted(value.Status); + SetRawResponse(response); + } + + private static bool GetHasCompleted(VectorStoreStatus status) + { + return status == VectorStoreStatus.Completed || + status == VectorStoreStatus.Expired; + } + + /// + /// Gets an instance representing an existing . + /// + /// A token that can be used to cancel this method call. + /// A representation of an existing . + public virtual async Task> GetVectorStoreAsync(CancellationToken cancellationToken = default) + { + ClientResult result + = await GetVectorStoreAsync(cancellationToken.ToRequestOptions()).ConfigureAwait(false); + return ClientResult.FromValue( + VectorStore.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + } + + /// + /// Gets an instance representing an existing . + /// + /// A token that can be used to cancel this method call. + /// A representation of an existing . + public virtual ClientResult GetVectorStore(CancellationToken cancellationToken = default) + { + ClientResult result = GetVectorStore(cancellationToken.ToRequestOptions()); + return ClientResult.FromValue(VectorStore.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + } +} diff --git a/src/Custom/VectorStores/Internal/AddFileToVectorStoreOperationToken.cs b/src/Custom/VectorStores/Internal/AddFileToVectorStoreOperationToken.cs new file mode 100644 index 00000000..1f8555eb --- /dev/null +++ b/src/Custom/VectorStores/Internal/AddFileToVectorStoreOperationToken.cs @@ -0,0 +1,101 @@ +using System; +using System.ClientModel; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +#nullable enable + +namespace OpenAI.VectorStores; + +internal class AddFileToVectorStoreOperationToken : ContinuationToken +{ + public AddFileToVectorStoreOperationToken(string vectorStoreId, string fileId) + { + VectorStoreId = vectorStoreId; + FileId = fileId; + } + + public string VectorStoreId { get; } + + public string FileId { get; } + + public override BinaryData ToBytes() + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + writer.WriteStartObject(); + + writer.WriteString("vectorStoreId", VectorStoreId); + writer.WriteString("fileId", FileId); + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); + } + + public static AddFileToVectorStoreOperationToken FromToken(ContinuationToken continuationToken) + { + if (continuationToken is AddFileToVectorStoreOperationToken token) + { + return token; + } + + BinaryData data = continuationToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create AddFileToVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + Utf8JsonReader reader = new(data); + + string vectorStoreId = null!; + string fileId = null!; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "vectorStoreId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + vectorStoreId = reader.GetString()!; + break; + + case "fileId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + fileId = reader.GetString()!; + break; + + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + if (vectorStoreId is null || fileId is null) + { + throw new ArgumentException("Failed to create AddFileToVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + return new(vectorStoreId, fileId); + } +} + diff --git a/src/Custom/VectorStores/Internal/CreateBatchFileJobOperationToken.cs b/src/Custom/VectorStores/Internal/CreateBatchFileJobOperationToken.cs new file mode 100644 index 00000000..20e47b4d --- /dev/null +++ b/src/Custom/VectorStores/Internal/CreateBatchFileJobOperationToken.cs @@ -0,0 +1,101 @@ +using System; +using System.ClientModel; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +#nullable enable + +namespace OpenAI.VectorStores; + +internal class CreateBatchFileJobOperationToken : ContinuationToken +{ + public CreateBatchFileJobOperationToken(string vectorStoreId, string batchId) + { + VectorStoreId = vectorStoreId; + BatchId = batchId; + } + + public string VectorStoreId { get; } + + public string BatchId { get; } + + public override BinaryData ToBytes() + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + writer.WriteStartObject(); + + writer.WriteString("vectorStoreId", VectorStoreId); + writer.WriteString("batchId", BatchId); + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); + } + + public static CreateBatchFileJobOperationToken FromToken(ContinuationToken continuationToken) + { + if (continuationToken is CreateBatchFileJobOperationToken token) + { + return token; + } + + BinaryData data = continuationToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create AddFileBatchToVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + Utf8JsonReader reader = new(data); + + string vectorStoreId = null!; + string batchId = null!; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "vectorStoreId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + vectorStoreId = reader.GetString()!; + break; + + case "batchId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + batchId = reader.GetString()!; + break; + + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + if (vectorStoreId is null || batchId is null) + { + throw new ArgumentException("Failed to create AddFileBatchToVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + return new(vectorStoreId, batchId); + } +} + diff --git a/src/Custom/VectorStores/Internal/CreateVectorStoreOperationToken.cs b/src/Custom/VectorStores/Internal/CreateVectorStoreOperationToken.cs new file mode 100644 index 00000000..00eca795 --- /dev/null +++ b/src/Custom/VectorStores/Internal/CreateVectorStoreOperationToken.cs @@ -0,0 +1,90 @@ +using System; +using System.ClientModel; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +#nullable enable + +namespace OpenAI.VectorStores; + +internal class CreateVectorStoreOperationToken : ContinuationToken +{ + public CreateVectorStoreOperationToken(string vectorStoreId) + { + VectorStoreId = vectorStoreId; + } + + public string VectorStoreId { get; } + + public override BinaryData ToBytes() + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + writer.WriteStartObject(); + + writer.WriteString("vectorStoreId", VectorStoreId); + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); + } + + public static CreateVectorStoreOperationToken FromToken(ContinuationToken continuationToken) + { + if (continuationToken is CreateVectorStoreOperationToken token) + { + return token; + } + + BinaryData data = continuationToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create CreateVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + Utf8JsonReader reader = new(data); + + string vectorStoreId = null!; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "vectorStoreId": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + vectorStoreId = reader.GetString()!; + break; + + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + if (vectorStoreId is null) + { + throw new ArgumentException("Failed to create CreateVectorStoreOperationToken from provided continuationToken.", nameof(continuationToken)); + } + + return new(vectorStoreId); + } +} + diff --git a/src/Custom/VectorStores/VectorStoreClient.Convenience.cs b/src/Custom/VectorStores/VectorStoreClient.Convenience.cs deleted file mode 100644 index d067da35..00000000 --- a/src/Custom/VectorStores/VectorStoreClient.Convenience.cs +++ /dev/null @@ -1,235 +0,0 @@ -using OpenAI.Files; -using System.ClientModel; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace OpenAI.VectorStores; - -public partial class VectorStoreClient -{ - /// - /// Modifies an existing vector store. - /// - /// The vector store to modify. - /// The new options to apply to the vector store. - /// The modified vector store instance. - public virtual Task> ModifyVectorStoreAsync(VectorStore vectorStore, VectorStoreModificationOptions options) - => ModifyVectorStoreAsync(vectorStore?.Id, options); - - /// - /// Modifies an existing vector store. - /// - /// The vector store to modify. - /// The new options to apply to the vector store. - /// The modified vector store instance. - public virtual ClientResult ModifyVectorStore(VectorStore vectorStore, VectorStoreModificationOptions options) - => ModifyVectorStore(vectorStore?.Id, options); - - /// - /// Gets an up-to-date instance of an existing vector store. - /// - /// The existing vector store instance to get an updated instance of. - /// The refreshed vector store instance. - public virtual Task> GetVectorStoreAsync(VectorStore vectorStore) - => GetVectorStoreAsync(vectorStore?.Id); - - /// - /// Gets an up-to-date instance of an existing vector store. - /// - /// The existing vector store instance to get an updated instance of. - /// The refreshed vector store instance. - public virtual ClientResult GetVectorStore(VectorStore vectorStore) - => GetVectorStore(vectorStore?.Id); - - /// - /// Deletes a vector store. - /// - /// The vector store to delete. - /// A instance. - public virtual Task> DeleteVectorStoreAsync(VectorStore vectorStore) - => DeleteVectorStoreAsync(vectorStore?.Id); - - /// - /// Deletes a vector store. - /// - /// The vector store to delete. - /// A instance. - public virtual ClientResult DeleteVectorStore(VectorStore vectorStore) - => DeleteVectorStore(vectorStore?.Id); - - /// - /// Associates an uploaded file with a vector store, beginning ingestion of the file into the vector store. - /// - /// The vector store to associate the file with. - /// The file to associate with the vector store. - /// - /// A instance that represents the new association. - /// - public virtual Task> AddFileToVectorStoreAsync(VectorStore vectorStore, OpenAIFile file) - => AddFileToVectorStoreAsync(vectorStore?.Id, file?.Id); - - /// - /// Associates an uploaded file with a vector store, beginning ingestion of the file into the vector store. - /// - /// The vector store to associate the file with. - /// The file to associate with the vector store. - /// - /// A instance that represents the new association. - /// - public virtual ClientResult AddFileToVectorStore(VectorStore vectorStore, OpenAIFile file) - => AddFileToVectorStore(vectorStore?.Id, file?.Id); - - /// - /// Gets a page collection holding instances that represent file inclusions in the - /// specified vector store. - /// - /// - /// The vector store to enumerate the file associations of. - /// - /// Options describing the collection to return. - /// A collection of . - public virtual AsyncCollectionResult GetFileAssociationsAsync( - VectorStore vectorStore, - VectorStoreFileAssociationCollectionOptions options = default) - => GetFileAssociationsAsync(vectorStore?.Id, options); - - /// - /// Gets a page collection holding instances that represent file inclusions in the - /// specified vector store. - /// - /// - /// The ID vector store to enumerate the file associations of. - /// - /// Options describing the collection to return. - /// A collection of . - public virtual CollectionResult GetFileAssociations( - VectorStore vectorStore, - VectorStoreFileAssociationCollectionOptions options = default) - => GetFileAssociations(vectorStore?.Id, options); - - /// - /// Gets a instance representing an existing association between a known - /// vector store and file. - /// - /// The vector store associated with the file. - /// The file associated with the vector store. - /// A instance. - public virtual Task> GetFileAssociationAsync( - VectorStore vectorStore, - OpenAIFile file) - => GetFileAssociationAsync(vectorStore?.Id, file?.Id); - - /// - /// Gets a instance representing an existing association between a known - /// vector store and file. - /// - /// The vector store associated with the file. - /// The file associated with the vector store. - /// A instance. - public virtual ClientResult GetFileAssociation( - VectorStore vectorStore, - OpenAIFile file) - => GetFileAssociation(vectorStore?.Id, file?.Id); - - /// - /// Removes the association between a file and vector store, which makes the file no longer available to the vector - /// store. - /// - /// - /// This does not delete the file. To delete the file, use . - /// - /// The vector store that the file should be removed from. - /// The file to remove from the vector store. - /// A instance. - public virtual Task> RemoveFileFromStoreAsync(VectorStore vectorStore, OpenAIFile file) - => RemoveFileFromStoreAsync(vectorStore?.Id, file?.Id); - - /// - /// Removes the association between a file and vector store, which makes the file no longer available to the vector - /// store. - /// - /// - /// This does not delete the file. To delete the file, use . - /// - /// The vector store that the file should be removed from. - /// The file to remove from the vector store. - /// A instance. - public virtual ClientResult RemoveFileFromStore(VectorStore vectorStore, OpenAIFile file) - => RemoveFileFromStore(vectorStore?.Id, file?.Id); - - /// - /// Begins a batch job to associate multiple jobs with a vector store, beginning the ingestion process. - /// - /// The vector store to associate files with. - /// The files to associate with the vector store. - /// A instance representing the batch operation. - public virtual Task> CreateBatchFileJobAsync(VectorStore vectorStore, IEnumerable files) - => CreateBatchFileJobAsync(vectorStore?.Id, files?.Select(file => file.Id)); - - /// - /// Begins a batch job to associate multiple jobs with a vector store, beginning the ingestion process. - /// - /// The vector store to associate files with. - /// The files to associate with the vector store. - /// A instance representing the batch operation. - public virtual ClientResult CreateBatchFileJob(VectorStore vectorStore, IEnumerable files) - => CreateBatchFileJob(vectorStore?.Id, files?.Select(file => file.Id)); - - /// - /// Gets an updated instance of an existing , refreshing its status. - /// - /// The job to refresh. - /// The refreshed instance of . - public virtual Task> GetBatchFileJobAsync(VectorStoreBatchFileJob batchJob) - => GetBatchFileJobAsync(batchJob?.VectorStoreId, batchJob?.BatchId); - - /// - /// Gets an updated instance of an existing , refreshing its status. - /// - /// The job to refresh. - /// The refreshed instance of . - public virtual ClientResult GetBatchFileJob(VectorStoreBatchFileJob batchJob) - => GetBatchFileJob(batchJob?.VectorStoreId, batchJob?.BatchId); - - /// - /// Cancels an in-progress . - /// - /// The that should be canceled. - /// An updated instance. - public virtual Task> CancelBatchFileJobAsync(VectorStoreBatchFileJob batchJob) - => CancelBatchFileJobAsync(batchJob?.VectorStoreId, batchJob?.BatchId); - - /// - /// Cancels an in-progress . - /// - /// The that should be canceled. - /// An updated instance. - public virtual ClientResult CancelBatchFileJob(VectorStoreBatchFileJob batchJob) - => CancelBatchFileJob(batchJob?.VectorStoreId, batchJob?.BatchId); - - /// - /// Gets a page collection holding file associations associated with a vector store batch file job, representing the files - /// that were scheduled for ingestion into the vector store. - /// - /// The vector store batch file job to retrieve file associations from. - /// Options describing the collection to return. - /// A collection of . - public virtual AsyncCollectionResult GetFileAssociationsAsync( - VectorStoreBatchFileJob batchJob, - VectorStoreFileAssociationCollectionOptions options = default) - => GetFileAssociationsAsync(batchJob?.VectorStoreId, batchJob?.BatchId, options); - - /// - /// Gets a page collection holding file associations associated with a vector store batch file job, representing the files - /// that were scheduled for ingestion into the vector store. - /// - /// The vector store batch file job to retrieve file associations from. - /// Options describing the collection to return. - /// A collection of . - public virtual CollectionResult GetFileAssociations( - VectorStoreBatchFileJob batchJob, - VectorStoreFileAssociationCollectionOptions options = default) - => GetFileAssociations(batchJob?.VectorStoreId, batchJob?.BatchId, options); - -} diff --git a/src/Custom/VectorStores/VectorStoreClient.Protocol.cs b/src/Custom/VectorStores/VectorStoreClient.Protocol.cs index af4fe491..b27f330c 100644 --- a/src/Custom/VectorStores/VectorStoreClient.Protocol.cs +++ b/src/Custom/VectorStores/VectorStoreClient.Protocol.cs @@ -87,30 +87,49 @@ public virtual CollectionResult GetVectorStores(int? limit, string order, string /// /// [Protocol Method] Creates a vector store. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. - public virtual async Task CreateVectorStoreAsync(BinaryContent content, RequestOptions options = null) + /// A that can be used to wait for + /// the vector store creation to complete. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task CreateVectorStoreAsync(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { using PipelineMessage message = CreateCreateVectorStoreRequest(content, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + PipelineResponse response = await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false); + VectorStore value = VectorStore.FromResponse(response); + + CreateVectorStoreOperation operation = this.CreateCreateVectorStoreOperation(ClientResult.FromValue(value, response)); + return await operation.WaitUntilAsync(waitUntilCompleted, options).ConfigureAwait(false); } /// /// [Protocol Method] Creates a vector store. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// is null. /// Service returned a non-success status code. - /// The response returned from the service. + /// A that can be used to wait for + /// the vector store creation to complete. [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult CreateVectorStore(BinaryContent content, RequestOptions options = null) + public virtual CreateVectorStoreOperation CreateVectorStore(BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { using PipelineMessage message = CreateCreateVectorStoreRequest(content, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + PipelineResponse response = _pipeline.ProcessMessage(message, options); + VectorStore value = VectorStore.FromResponse(response); + + CreateVectorStoreOperation operation = this.CreateCreateVectorStoreOperation(ClientResult.FromValue(value, response)); + return operation.WaitUntil(waitUntilCompleted, options); } /// @@ -122,8 +141,7 @@ public virtual ClientResult CreateVectorStore(BinaryContent content, RequestOpti /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual async Task GetVectorStoreAsync(string vectorStoreId, RequestOptions options) + internal virtual async Task GetVectorStoreAsync(string vectorStoreId, RequestOptions options) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); @@ -140,8 +158,7 @@ public virtual async Task GetVectorStoreAsync(string vectorStoreId /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult GetVectorStore(string vectorStoreId, RequestOptions options) + internal virtual ClientResult GetVectorStore(string vectorStoreId, RequestOptions options) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); @@ -297,44 +314,63 @@ public virtual CollectionResult GetFileAssociations(string vectorStoreId, int? l return new VectorStoreFileCollectionResult(this, _pipeline, options, vectorStoreId, limit, order, after, before, filter); } + /// /// [Protocol Method] Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object). /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store for which to create a File. /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// or is null. /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. - /// The response returned from the service. + /// A that can be used to wait for + /// the vector store file addition to complete. [EditorBrowsable(EditorBrowsableState.Never)] - public virtual async Task AddFileToVectorStoreAsync(string vectorStoreId, BinaryContent content, RequestOptions options = null) + public virtual async Task AddFileToVectorStoreAsync(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateVectorStoreFileRequest(vectorStoreId, content, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + PipelineResponse response = await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + AddFileToVectorStoreOperation operation = this.CreateAddFileToVectorStoreOperation(ClientResult.FromValue(value, response)); + return await operation.WaitUntilAsync(waitUntilCompleted, options).ConfigureAwait(false); } /// /// [Protocol Method] Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object). /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store for which to create a File. /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// or is null. /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. - /// The response returned from the service. + /// A that can be used to wait for + /// the vector store file addition to complete. [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult AddFileToVectorStore(string vectorStoreId, BinaryContent content, RequestOptions options = null) + public virtual AddFileToVectorStoreOperation AddFileToVectorStore(string vectorStoreId, BinaryContent content, bool waitUntilCompleted, RequestOptions options = null) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateVectorStoreFileRequest(vectorStoreId, content, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + PipelineResponse response = _pipeline.ProcessMessage(message, options); + VectorStoreFileAssociation value = VectorStoreFileAssociation.FromResponse(response); + + AddFileToVectorStoreOperation operation = this.CreateAddFileToVectorStoreOperation(ClientResult.FromValue(value, response)); + return operation.WaitUntil(waitUntilCompleted, options); } /// @@ -377,6 +413,122 @@ public virtual ClientResult GetFileAssociation(string vectorStoreId, string file return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); } + /// + /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + /// + /// The ID of the vector store that the file batch belongs to. + /// The ID of the file batch to cancel. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual async Task CancelBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + + using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(vectorStoreId, batchId, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + } + + /// + /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + /// + /// The ID of the vector store that the file batch belongs to. + /// The ID of the file batch to cancel. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// The response returned from the service. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchId, RequestOptions options) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + + using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(vectorStoreId, batchId, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + } + + /// + /// [Protocol Method] Returns a paginated collection of vector store files in a batch. + /// + /// The ID of the vector store that the file batch belongs to. + /// The ID of the file batch that the files belong to. + /// + /// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + /// default is 20. + /// + /// + /// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc` + /// for descending order. Allowed values: "asc" | "desc" + /// + /// + /// 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. + /// + /// + /// 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. + /// + /// Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// A collection of service responses, each holding a page of values. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual AsyncCollectionResult GetFileAssociationsAsync(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + + return new AsyncVectorStoreFileBatchCollectionResult(this, _pipeline, options, vectorStoreId, batchId, limit, order, after, before, filter); + } + + /// + /// [Protocol Method] Returns a paginated collection of vector store files in a batch. + /// + /// The ID of the vector store that the file batch belongs to. + /// The ID of the file batch that the files belong to. + /// + /// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + /// default is 20. + /// + /// + /// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc` + /// for descending order. Allowed values: "asc" | "desc" + /// + /// + /// 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. + /// + /// + /// 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. + /// + /// Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// A collection of service responses, each holding a page of values. + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual CollectionResult GetFileAssociations(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + + return new VectorStoreFileBatchCollectionResult(this, _pipeline, options, vectorStoreId, batchId, limit, order, after, before, filter); + } + /// /// [Protocol Method] Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint. /// @@ -420,41 +572,67 @@ public virtual ClientResult RemoveFileFromStore(string vectorStoreId, string fil /// /// [Protocol Method] Create a vector store file batch. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store for which to create a file batch. /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// or is null. /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. - /// The response returned from the service. + /// A that can be used to wait for + /// the operation to complete, get information about the batch file job, or cancel the operation. [EditorBrowsable(EditorBrowsableState.Never)] - public virtual async Task CreateBatchFileJobAsync(string vectorStoreId, BinaryContent content, RequestOptions options = null) + public virtual async Task CreateBatchFileJobAsync( + string vectorStoreId, + BinaryContent content, + bool waitUntilCompleted, + RequestOptions options = null) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateVectorStoreFileBatchRequest(vectorStoreId, content, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + PipelineResponse response = await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false); + VectorStoreBatchFileJob job = VectorStoreBatchFileJob.FromResponse(response); + + CreateBatchFileJobOperation operation = this.CreateBatchFileJobOperation(ClientResult.FromValue(job, response)); + return await operation.WaitUntilAsync(waitUntilCompleted, options).ConfigureAwait(false); } /// /// [Protocol Method] Create a vector store file batch. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store for which to create a file batch. /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. /// or is null. /// is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. - /// The response returned from the service. + /// A that can be used to wait for + /// the operation to complete, get information about the batch file job, or cancel the operation. [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult CreateBatchFileJob(string vectorStoreId, BinaryContent content, RequestOptions options = null) + public virtual CreateBatchFileJobOperation CreateBatchFileJob( + string vectorStoreId, + BinaryContent content, + bool waitUntilCompleted, + RequestOptions options = null) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNull(content, nameof(content)); using PipelineMessage message = CreateCreateVectorStoreFileBatchRequest(vectorStoreId, content, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + PipelineResponse response = _pipeline.ProcessMessage(message, options); + VectorStoreBatchFileJob job = VectorStoreBatchFileJob.FromResponse(response); + + CreateBatchFileJobOperation operation = this.CreateBatchFileJobOperation(ClientResult.FromValue(job, response)); + return operation.WaitUntil(waitUntilCompleted, options); } /// @@ -467,8 +645,7 @@ public virtual ClientResult CreateBatchFileJob(string vectorStoreId, BinaryConte /// or is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual async Task GetBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options) + internal virtual async Task GetBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); @@ -487,8 +664,7 @@ public virtual async Task GetBatchFileJobAsync(string vectorStoreI /// or is an empty string, and was expected to be non-empty. /// Service returned a non-success status code. /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchId, RequestOptions options) + internal virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchId, RequestOptions options) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); @@ -497,119 +673,288 @@ public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchId return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); } - /// - /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. - /// - /// The ID of the vector store that the file batch belongs to. - /// The ID of the file batch to cancel. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// or is null. - /// or is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual async Task CancelBatchFileJobAsync(string vectorStoreId, string batchId, RequestOptions options) + internal virtual PipelineMessage CreateCreateVectorStoreRequest(BinaryContent content, RequestOptions options) { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; + } - using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(vectorStoreId, batchId, options); - return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); + internal virtual PipelineMessage CreateGetVectorStoresRequest(int? limit, string order, string after, string before, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores", false); + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + if (order != null) + { + uri.AppendQuery("order", order, true); + } + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (before != null) + { + uri.AppendQuery("before", before, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } - /// - /// [Protocol Method] Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. - /// - /// The ID of the vector store that the file batch belongs to. - /// The ID of the file batch to cancel. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// or is null. - /// or is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// The response returned from the service. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchId, RequestOptions options) + internal virtual PipelineMessage CreateGetVectorStoreRequest(string vectorStoreId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } - using PipelineMessage message = CreateCancelVectorStoreFileBatchRequest(vectorStoreId, batchId, options); - return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); + internal virtual PipelineMessage CreateModifyVectorStoreRequest(string vectorStoreId, BinaryContent content, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; } - /// - /// [Protocol Method] Returns a paginated collection of vector store files in a batch. - /// - /// The ID of the vector store that the file batch belongs to. - /// The ID of the file batch that the files belong to. - /// - /// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the - /// default is 20. - /// - /// - /// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc` - /// for descending order. Allowed values: "asc" | "desc" - /// - /// - /// 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. - /// - /// - /// 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. - /// - /// Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// or is null. - /// or is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// A collection of service responses, each holding a page of values. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual AsyncCollectionResult GetFileAssociationsAsync(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) + internal virtual PipelineMessage CreateDeleteVectorStoreRequest(string vectorStoreId, RequestOptions options) { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "DELETE"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } - return new AsyncVectorStoreFileBatchCollectionResult(this, _pipeline, options, vectorStoreId, batchId, limit, order, after, before, filter); + internal virtual PipelineMessage CreateGetVectorStoreFilesRequest(string vectorStoreId, int? limit, string order, string after, string before, string filter, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/files", false); + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + if (order != null) + { + uri.AppendQuery("order", order, true); + } + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (before != null) + { + uri.AppendQuery("before", before, true); + } + if (filter != null) + { + uri.AppendQuery("filter", filter, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } - /// - /// [Protocol Method] Returns a paginated collection of vector store files in a batch. - /// - /// The ID of the vector store that the file batch belongs to. - /// The ID of the file batch that the files belong to. - /// - /// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the - /// default is 20. - /// - /// - /// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc` - /// for descending order. Allowed values: "asc" | "desc" - /// - /// - /// 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. - /// - /// - /// 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. - /// - /// Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. - /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// or is null. - /// or is an empty string, and was expected to be non-empty. - /// Service returned a non-success status code. - /// A collection of service responses, each holding a page of values. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual CollectionResult GetFileAssociations(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) + internal virtual PipelineMessage CreateCreateVectorStoreFileRequest(string vectorStoreId, BinaryContent content, RequestOptions options) { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchId, nameof(batchId)); + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/files", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; + } - return new VectorStoreFileBatchCollectionResult(this, _pipeline, options, vectorStoreId, batchId, limit, order, after, before, filter); + internal virtual PipelineMessage CreateDeleteVectorStoreFileRequest(string vectorStoreId, string fileId, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "DELETE"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/files/", false); + uri.AppendPath(fileId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateCancelVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches/", false); + uri.AppendPath(batchId, true); + uri.AppendPath("/cancel", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateCreateVectorStoreFileBatchRequest(string vectorStoreId, BinaryContent content, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "POST"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches", false); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + request.Content = content; + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateGetVectorStoreFileRequest(string vectorStoreId, string fileId, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/files/", false); + uri.AppendPath(fileId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateGetVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches/", false); + uri.AppendPath(batchId, true); + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; + } + + internal virtual PipelineMessage CreateGetFilesInVectorStoreBatchesRequest(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) + { + var message = _pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + var request = message.Request; + request.Method = "GET"; + var uri = new ClientUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/vector_stores/", false); + uri.AppendPath(vectorStoreId, true); + uri.AppendPath("/file_batches/", false); + uri.AppendPath(batchId, true); + uri.AppendPath("/files", false); + if (limit != null) + { + uri.AppendQuery("limit", limit.Value, true); + } + if (order != null) + { + uri.AppendQuery("order", order, true); + } + if (after != null) + { + uri.AppendQuery("after", after, true); + } + if (before != null) + { + uri.AppendQuery("before", before, true); + } + if (filter != null) + { + uri.AppendQuery("filter", filter, true); + } + request.Uri = uri.ToUri(); + request.Headers.Set("Accept", "application/json"); + message.Apply(options); + return message; } } diff --git a/src/Custom/VectorStores/VectorStoreClient.cs b/src/Custom/VectorStores/VectorStoreClient.cs index a7d76d4c..9ee6e23c 100644 --- a/src/Custom/VectorStores/VectorStoreClient.cs +++ b/src/Custom/VectorStores/VectorStoreClient.cs @@ -12,6 +12,7 @@ namespace OpenAI.VectorStores; /// /// The service client for OpenAI vector store operations. /// +[Experimental("OPENAI001")] [CodeGenClient("VectorStores")] [CodeGenSuppress("VectorStoreClient", typeof(ClientPipeline), typeof(ApiKeyCredential), typeof(Uri))] [CodeGenSuppress("CreateVectorStoreAsync", typeof(VectorStoreCreationOptions))] @@ -40,7 +41,6 @@ namespace OpenAI.VectorStores; [CodeGenSuppress("CancelVectorStoreFileBatch", typeof(string), typeof(string))] [CodeGenSuppress("GetFilesInVectorStoreBatchesAsync", typeof(string), typeof(string), typeof(int?), typeof(InternalListFilesInVectorStoreBatchRequestOrder?), typeof(string), typeof(string), typeof(VectorStoreFileStatusFilter?))] [CodeGenSuppress("GetFilesInVectorStoreBatches", typeof(string), typeof(string), typeof(int?), typeof(InternalListFilesInVectorStoreBatchRequestOrder?), typeof(string), typeof(string), typeof(VectorStoreFileStatusFilter?))] -[Experimental("OPENAI001")] public partial class VectorStoreClient { // CUSTOM: Remove virtual keyword. @@ -57,15 +57,6 @@ public partial class VectorStoreClient { } - // CUSTOM: Added as a convenience. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. - public VectorStoreClient(string apiKey, OpenAIClientOptions options) : this(new ApiKeyCredential(apiKey), options) - { - } - // CUSTOM: // - Used a custom pipeline. // - Demoted the endpoint parameter to be a property in the options class. @@ -76,13 +67,12 @@ public VectorStoreClient(string apiKey, OpenAIClientOptions options) : this(new { } - // CUSTOM: - // - Used a custom pipeline. - // - Demoted the endpoint parameter to be a property in the options class. - /// Initializes a new instance of . - /// The API key to authenticate with the service. - /// The options to configure the client. - /// is null. + /// + /// Initializes a new instance of that will use an API key when authenticating. + /// + /// The API key used to authenticate with the service endpoint. + /// Additional options to customize the client. + /// The provided was null. public VectorStoreClient(ApiKeyCredential credential, OpenAIClientOptions options) { Argument.AssertNotNull(credential, nameof(credential)); @@ -109,28 +99,51 @@ protected internal VectorStoreClient(ClientPipeline pipeline, OpenAIClientOption _endpoint = OpenAIClient.GetEndpoint(options); } + internal virtual CreateVectorStoreOperation CreateCreateVectorStoreOperation(ClientResult result) + { + return new CreateVectorStoreOperation(_pipeline, _endpoint, result); + } + + internal virtual AddFileToVectorStoreOperation CreateAddFileToVectorStoreOperation(ClientResult result) + { + return new AddFileToVectorStoreOperation(Pipeline, _endpoint, result); + } + + internal virtual CreateBatchFileJobOperation CreateBatchFileJobOperation(ClientResult result) + { + return new CreateBatchFileJobOperation(Pipeline, _endpoint, result); + } + /// Creates a vector store. + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The to use. /// A token that can be used to cancel this method call. /// is null. - /// Create vector store. - public virtual async Task> CreateVectorStoreAsync(VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the vector store creation to complete. + public virtual async Task CreateVectorStoreAsync(bool waitUntilCompleted, VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default) { using BinaryContent content = vectorStore?.ToBinaryContent(); - ClientResult result = await CreateVectorStoreAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false); - return ClientResult.FromValue(VectorStore.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + return await CreateVectorStoreAsync(content, waitUntilCompleted, cancellationToken.ToRequestOptions()).ConfigureAwait(false); } /// Creates a vector store. + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The to use. /// A token that can be used to cancel this method call. /// is null. - /// Create vector store. - public virtual ClientResult CreateVectorStore(VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the vector store creation to complete. + public virtual CreateVectorStoreOperation CreateVectorStore(bool waitUntilCompleted, VectorStoreCreationOptions vectorStore = null, CancellationToken cancellationToken = default) { using BinaryContent content = vectorStore?.ToBinaryContent(); - ClientResult result = CreateVectorStore(content, cancellationToken.ToRequestOptions()); - return ClientResult.FromValue(VectorStore.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + return CreateVectorStore(content, waitUntilCompleted, cancellationToken.ToRequestOptions()); } /// @@ -139,7 +152,7 @@ public virtual ClientResult CreateVectorStore(VectorStoreCreationOp /// The ID of the vector store to retrieve. /// A token that can be used to cancel this method call. /// A representation of an existing . - public virtual async Task> GetVectorStoreAsync(string vectorStoreId, CancellationToken cancellationToken = default) + internal virtual async Task> GetVectorStoreAsync(string vectorStoreId, CancellationToken cancellationToken = default) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); @@ -294,37 +307,45 @@ public virtual CollectionResult GetVectorStores( /// /// Associates a single, uploaded file with a vector store, beginning ingestion of the file into the vector store. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store to associate the file with. /// The ID of the file to associate with the vector store. /// A token that can be used to cancel this method call. - /// - /// A instance that represents the new association. - /// - public virtual async Task> AddFileToVectorStoreAsync(string vectorStoreId, string fileId, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the vector store file addition to complete. + /// or is null. + public virtual async Task AddFileToVectorStoreAsync(string vectorStoreId, string fileId, bool waitUntilCompleted, CancellationToken cancellationToken = default) { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(fileId, nameof(fileId)); + InternalCreateVectorStoreFileRequest internalRequest = new(fileId); - ClientResult protocolResult = await AddFileToVectorStoreAsync(vectorStoreId, internalRequest.ToBinaryContent(), cancellationToken.ToRequestOptions()).ConfigureAwait(false); - PipelineResponse protocolResponse = protocolResult?.GetRawResponse(); - VectorStoreFileAssociation fileAssociation = VectorStoreFileAssociation.FromResponse(protocolResponse); - return ClientResult.FromValue(fileAssociation, protocolResponse); + return await AddFileToVectorStoreAsync(vectorStoreId, internalRequest.ToBinaryContent(), waitUntilCompleted, cancellationToken.ToRequestOptions()).ConfigureAwait(false); } /// /// Associates a single, uploaded file with a vector store, beginning ingestion of the file into the vector store. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store to associate the file with. /// The ID of the file to associate with the vector store. /// A token that can be used to cancel this method call. - /// - /// A instance that represents the new association. - /// - public virtual ClientResult AddFileToVectorStore(string vectorStoreId, string fileId, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the vector store file addition to complete. + /// or is null. + public virtual AddFileToVectorStoreOperation AddFileToVectorStore(string vectorStoreId, string fileId, bool waitUntilCompleted, CancellationToken cancellationToken = default) { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(fileId, nameof(fileId)); + InternalCreateVectorStoreFileRequest internalRequest = new(fileId); - ClientResult protocolResult = AddFileToVectorStore(vectorStoreId, internalRequest.ToBinaryContent(), cancellationToken.ToRequestOptions()); - PipelineResponse protocolResponse = protocolResult?.GetRawResponse(); - VectorStoreFileAssociation fileAssociation = VectorStoreFileAssociation.FromResponse(protocolResponse); - return ClientResult.FromValue(fileAssociation, protocolResponse); + return AddFileToVectorStore(vectorStoreId, internalRequest.ToBinaryContent(), waitUntilCompleted, cancellationToken.ToRequestOptions()); } /// @@ -486,121 +507,57 @@ public virtual ClientResult RemoveFileFromStore(stri } /// - /// Begins a batch job to associate multiple jobs with a vector store, beginning the ingestion process. + /// Adds multiple files in a batch to the vector store, beginning the ingestion process. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store to associate files with. /// The IDs of the files to associate with the vector store. /// A token that can be used to cancel this method call. - /// A instance representing the batch operation. - public virtual async Task> CreateBatchFileJobAsync(string vectorStoreId, IEnumerable fileIds, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the operation to complete, get information about the batch file job, or cancel the operation. + public virtual async Task CreateBatchFileJobAsync( + string vectorStoreId, + IEnumerable fileIds, + bool waitUntilCompleted, + CancellationToken cancellationToken = default) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNullOrEmpty(fileIds, nameof(fileIds)); BinaryContent content = new InternalCreateVectorStoreFileBatchRequest(fileIds).ToBinaryContent(); - ClientResult result = await CreateBatchFileJobAsync(vectorStoreId, content, cancellationToken.ToRequestOptions()).ConfigureAwait(false); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); + RequestOptions options = cancellationToken.ToRequestOptions(); + + return await CreateBatchFileJobAsync(vectorStoreId, content, waitUntilCompleted, options).ConfigureAwait(false); } /// /// Begins a batch job to associate multiple jobs with a vector store, beginning the ingestion process. /// + /// Value indicating whether the method + /// should return after the operation has been started and is still running + /// on the service, or wait until the operation has completed to return. + /// /// The ID of the vector store to associate files with. /// The IDs of the files to associate with the vector store. /// A token that can be used to cancel this method call. - /// A instance representing the batch operation. - public virtual ClientResult CreateBatchFileJob(string vectorStoreId, IEnumerable fileIds, CancellationToken cancellationToken = default) + /// A that can be used to wait for + /// the operation to complete, get information about the batch file job, or cancel the operation. + public virtual CreateBatchFileJobOperation CreateBatchFileJob( + string vectorStoreId, + IEnumerable fileIds, + bool waitUntilCompleted, + CancellationToken cancellationToken = default) { Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); Argument.AssertNotNullOrEmpty(fileIds, nameof(fileIds)); BinaryContent content = new InternalCreateVectorStoreFileBatchRequest(fileIds).ToBinaryContent(); - ClientResult result = CreateBatchFileJob(vectorStoreId, content, cancellationToken.ToRequestOptions()); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); - } - - /// - /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. - /// - /// The ID of the vector store into which the batch of files was started. - /// The ID of the batch operation adding files to the vector store. - /// A token that can be used to cancel this method call. - /// A instance representing the ingestion operation. - public virtual async Task> GetBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) - { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); - - ClientResult result = await GetBatchFileJobAsync(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); - } - - /// - /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. - /// - /// The ID of the vector store into which the batch of files was started. - /// The ID of the batch operation adding files to the vector store. - /// A token that can be used to cancel this method call. - /// A instance representing the ingestion operation. - public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) - { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); + RequestOptions options = cancellationToken.ToRequestOptions(); - ClientResult result = GetBatchFileJob(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); - } - - /// - /// Cancels an in-progress . - /// - /// - /// The ID of the that is the ingestion target of the batch job being cancelled. - /// - /// - /// The ID of the that should be canceled. - /// - /// A token that can be used to cancel this method call. - /// An updated instance. - public virtual async Task> CancelBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) - { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); - - ClientResult result = await CancelBatchFileJobAsync(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); - } - - /// - /// Cancels an in-progress . - /// - /// - /// The ID of the that is the ingestion target of the batch job being cancelled. - /// - /// - /// The ID of the that should be canceled. - /// - /// A token that can be used to cancel this method call. - /// An updated instance. - public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) - { - Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); - Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); - - ClientResult result = CancelBatchFileJob(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()); - PipelineResponse response = result?.GetRawResponse(); - VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); - return ClientResult.FromValue(value, response); + return CreateBatchFileJob(vectorStoreId, content, waitUntilCompleted, options); } /// @@ -735,4 +692,84 @@ public virtual CollectionResult GetFileAssociations( return GetFileAssociations(vectorStoreId, batchJobId, pageToken?.Limit, pageToken?.Order, pageToken?.After, pageToken?.Before, pageToken?.Filter, cancellationToken.ToRequestOptions()) as CollectionResult; } + + /// + /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. + /// + /// The ID of the vector store into which the batch of files was started. + /// The ID of the batch operation adding files to the vector store. + /// A token that can be used to cancel this method call. + /// A instance representing the ingestion operation. + public virtual async Task> GetBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); + + ClientResult result = await GetBatchFileJobAsync(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result?.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Gets an existing vector store batch file ingestion job from a known vector store ID and job ID. + /// + /// The ID of the vector store into which the batch of files was started. + /// The ID of the batch operation adding files to the vector store. + /// A token that can be used to cancel this method call. + /// A instance representing the ingestion operation. + public virtual ClientResult GetBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); + + ClientResult result = GetBatchFileJob(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result?.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Cancels an in-progress . + /// + /// + /// The ID of the that is the ingestion target of the batch job being cancelled. + /// + /// + /// The ID of the that should be canceled. + /// + /// A token that can be used to cancel this method call. + /// An updated instance. + public virtual async Task> CancelBatchFileJobAsync(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); + + ClientResult result = await CancelBatchFileJobAsync(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()).ConfigureAwait(false); + PipelineResponse response = result?.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } + + /// + /// Cancels an in-progress . + /// + /// + /// The ID of the that is the ingestion target of the batch job being cancelled. + /// + /// + /// The ID of the that should be canceled. + /// + /// A token that can be used to cancel this method call. + /// An updated instance. + public virtual ClientResult CancelBatchFileJob(string vectorStoreId, string batchJobId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(vectorStoreId, nameof(vectorStoreId)); + Argument.AssertNotNullOrEmpty(batchJobId, nameof(batchJobId)); + + ClientResult result = CancelBatchFileJob(vectorStoreId, batchJobId, cancellationToken.ToRequestOptions()); + PipelineResponse response = result?.GetRawResponse(); + VectorStoreBatchFileJob value = VectorStoreBatchFileJob.FromResponse(response); + return ClientResult.FromValue(value, response); + } } diff --git a/src/Generated/BatchClient.cs b/src/Generated/BatchClient.cs index b9cca2ee..ab9059d8 100644 --- a/src/Generated/BatchClient.cs +++ b/src/Generated/BatchClient.cs @@ -23,79 +23,6 @@ protected BatchClient() { } - internal PipelineMessage CreateCreateBatchRequest(BinaryContent content, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/batches", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } - - internal PipelineMessage CreateGetBatchesRequest(string after, int? limit, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/batches", false); - if (after != null) - { - uri.AppendQuery("after", after, true); - } - if (limit != null) - { - uri.AppendQuery("limit", limit.Value, true); - } - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateRetrieveBatchRequest(string batchId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/batches/", false); - uri.AppendPath(batchId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateCancelBatchRequest(string batchId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/batches/", false); - uri.AppendPath(batchId, true); - uri.AppendPath("/cancel", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - private static PipelineMessageClassifier _pipelineMessageClassifier200; private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); } diff --git a/src/Generated/FineTuningClient.cs b/src/Generated/FineTuningClient.cs index 79ffc1d8..5f7d520c 100644 --- a/src/Generated/FineTuningClient.cs +++ b/src/Generated/FineTuningClient.cs @@ -22,62 +22,6 @@ protected FineTuningClient() { } - internal PipelineMessage CreateCreateFineTuningJobRequest(BinaryContent content, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/fine_tuning/jobs", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } - - internal PipelineMessage CreateGetPaginatedFineTuningJobsRequest(string after, int? limit, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/fine_tuning/jobs", false); - if (after != null) - { - uri.AppendQuery("after", after, true); - } - if (limit != null) - { - uri.AppendQuery("limit", limit.Value, true); - } - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateRetrieveFineTuningJobRequest(string fineTuningJobId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/fine_tuning/jobs/", false); - uri.AppendPath(fineTuningJobId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - internal PipelineMessage CreateCancelFineTuningJobRequest(string fineTuningJobId, RequestOptions options) { var message = _pipeline.CreateMessage(); diff --git a/src/Generated/Models/AudioTranscriptionFormat.Serialization.cs b/src/Generated/Models/AudioTranscriptionFormat.Serialization.cs deleted file mode 100644 index 699760d2..00000000 --- a/src/Generated/Models/AudioTranscriptionFormat.Serialization.cs +++ /dev/null @@ -1,31 +0,0 @@ -// - -#nullable disable - -using System; - -namespace OpenAI.Audio -{ - internal static partial class AudioTranscriptionFormatExtensions - { - public static string ToSerialString(this AudioTranscriptionFormat value) => value switch - { - AudioTranscriptionFormat.Simple => "json", - AudioTranscriptionFormat.Text => "text", - AudioTranscriptionFormat.Srt => "srt", - AudioTranscriptionFormat.Verbose => "verbose_json", - AudioTranscriptionFormat.Vtt => "vtt", - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown AudioTranscriptionFormat value.") - }; - - public static AudioTranscriptionFormat ToAudioTranscriptionFormat(this string value) - { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "json")) return AudioTranscriptionFormat.Simple; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "text")) return AudioTranscriptionFormat.Text; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "srt")) return AudioTranscriptionFormat.Srt; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "verbose_json")) return AudioTranscriptionFormat.Verbose; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "vtt")) return AudioTranscriptionFormat.Vtt; - throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown AudioTranscriptionFormat value."); - } - } -} diff --git a/src/Generated/Models/AudioTranscriptionFormat.cs b/src/Generated/Models/AudioTranscriptionFormat.cs new file mode 100644 index 00000000..7903416b --- /dev/null +++ b/src/Generated/Models/AudioTranscriptionFormat.cs @@ -0,0 +1,36 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace OpenAI.Audio +{ + public readonly partial struct AudioTranscriptionFormat : IEquatable + { + private readonly string _value; + + public AudioTranscriptionFormat(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string SimpleValue = "json"; + private const string TextValue = "text"; + private const string SrtValue = "srt"; + private const string VerboseValue = "verbose_json"; + private const string VttValue = "vtt"; + public static bool operator ==(AudioTranscriptionFormat left, AudioTranscriptionFormat right) => left.Equals(right); + public static bool operator !=(AudioTranscriptionFormat left, AudioTranscriptionFormat right) => !left.Equals(right); + public static implicit operator AudioTranscriptionFormat(string value) => new AudioTranscriptionFormat(value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is AudioTranscriptionFormat other && Equals(other); + public bool Equals(AudioTranscriptionFormat other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + public override string ToString() => _value; + } +} diff --git a/src/Generated/Models/AudioTranscriptionOptions.Serialization.cs b/src/Generated/Models/AudioTranscriptionOptions.Serialization.cs index cebd9176..0c168a6d 100644 --- a/src/Generated/Models/AudioTranscriptionOptions.Serialization.cs +++ b/src/Generated/Models/AudioTranscriptionOptions.Serialization.cs @@ -52,18 +52,18 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelRea if (SerializedAdditionalRawData?.ContainsKey("response_format") != true && Optional.IsDefined(ResponseFormat)) { writer.WritePropertyName("response_format"u8); - writer.WriteStringValue(ResponseFormat.Value.ToSerialString()); + writer.WriteStringValue(ResponseFormat.Value.ToString()); } if (SerializedAdditionalRawData?.ContainsKey("temperature") != true && Optional.IsDefined(Temperature)) { writer.WritePropertyName("temperature"u8); writer.WriteNumberValue(Temperature.Value); } - if (SerializedAdditionalRawData?.ContainsKey("timestamp_granularities") != true && Optional.IsCollectionDefined(TimestampGranularities)) + if (SerializedAdditionalRawData?.ContainsKey("timestamp_granularities") != true && Optional.IsCollectionDefined(InternalTimestampGranularities)) { writer.WritePropertyName("timestamp_granularities"u8); writer.WriteStartArray(); - foreach (var item in TimestampGranularities) + foreach (var item in InternalTimestampGranularities) { if (item == null) { @@ -160,7 +160,7 @@ internal static AudioTranscriptionOptions DeserializeAudioTranscriptionOptions(J { continue; } - responseFormat = property.Value.GetString().ToAudioTranscriptionFormat(); + responseFormat = new AudioTranscriptionFormat(property.Value.GetString()); continue; } if (property.NameEquals("temperature"u8)) @@ -241,15 +241,15 @@ internal virtual MultipartFormDataBinaryContent ToMultipartBinaryBody() } if (Optional.IsDefined(ResponseFormat)) { - content.Add(ResponseFormat.Value.ToSerialString(), "response_format"); + content.Add(ResponseFormat.Value.ToString(), "response_format"); } if (Optional.IsDefined(Temperature)) { content.Add(Temperature.Value, "temperature"); } - if (Optional.IsCollectionDefined(TimestampGranularities)) + if (Optional.IsCollectionDefined(InternalTimestampGranularities)) { - foreach (BinaryData item in TimestampGranularities) + foreach (BinaryData item in InternalTimestampGranularities) { content.Add(item, "timestamp_granularities", "timestamp_granularities"); } diff --git a/src/Generated/Models/AudioTranscriptionOptions.cs b/src/Generated/Models/AudioTranscriptionOptions.cs index 3640e680..041086b2 100644 --- a/src/Generated/Models/AudioTranscriptionOptions.cs +++ b/src/Generated/Models/AudioTranscriptionOptions.cs @@ -11,7 +11,7 @@ public partial class AudioTranscriptionOptions { internal IDictionary SerializedAdditionalRawData { get; set; } - internal AudioTranscriptionOptions(BinaryData file, InternalCreateTranscriptionRequestModel model, string language, string prompt, AudioTranscriptionFormat? responseFormat, float? temperature, IList timestampGranularities, IDictionary serializedAdditionalRawData) + internal AudioTranscriptionOptions(BinaryData file, InternalCreateTranscriptionRequestModel model, string language, string prompt, AudioTranscriptionFormat? responseFormat, float? temperature, IList internalTimestampGranularities, IDictionary serializedAdditionalRawData) { File = file; Model = model; @@ -19,7 +19,7 @@ internal AudioTranscriptionOptions(BinaryData file, InternalCreateTranscriptionR Prompt = prompt; ResponseFormat = responseFormat; Temperature = temperature; - TimestampGranularities = timestampGranularities; + InternalTimestampGranularities = internalTimestampGranularities; SerializedAdditionalRawData = serializedAdditionalRawData; } public string Language { get; set; } diff --git a/src/Generated/Models/AudioTranslationFormat.Serialization.cs b/src/Generated/Models/AudioTranslationFormat.Serialization.cs deleted file mode 100644 index 6c55e965..00000000 --- a/src/Generated/Models/AudioTranslationFormat.Serialization.cs +++ /dev/null @@ -1,31 +0,0 @@ -// - -#nullable disable - -using System; - -namespace OpenAI.Audio -{ - internal static partial class AudioTranslationFormatExtensions - { - public static string ToSerialString(this AudioTranslationFormat value) => value switch - { - AudioTranslationFormat.Simple => "json", - AudioTranslationFormat.Text => "text", - AudioTranslationFormat.Srt => "srt", - AudioTranslationFormat.Verbose => "verbose_json", - AudioTranslationFormat.Vtt => "vtt", - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown AudioTranslationFormat value.") - }; - - public static AudioTranslationFormat ToAudioTranslationFormat(this string value) - { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "json")) return AudioTranslationFormat.Simple; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "text")) return AudioTranslationFormat.Text; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "srt")) return AudioTranslationFormat.Srt; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "verbose_json")) return AudioTranslationFormat.Verbose; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "vtt")) return AudioTranslationFormat.Vtt; - throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown AudioTranslationFormat value."); - } - } -} diff --git a/src/Generated/Models/AudioTranslationFormat.cs b/src/Generated/Models/AudioTranslationFormat.cs new file mode 100644 index 00000000..bcd682ce --- /dev/null +++ b/src/Generated/Models/AudioTranslationFormat.cs @@ -0,0 +1,36 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace OpenAI.Audio +{ + public readonly partial struct AudioTranslationFormat : IEquatable + { + private readonly string _value; + + public AudioTranslationFormat(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string SimpleValue = "json"; + private const string TextValue = "text"; + private const string SrtValue = "srt"; + private const string VerboseValue = "verbose_json"; + private const string VttValue = "vtt"; + public static bool operator ==(AudioTranslationFormat left, AudioTranslationFormat right) => left.Equals(right); + public static bool operator !=(AudioTranslationFormat left, AudioTranslationFormat right) => !left.Equals(right); + public static implicit operator AudioTranslationFormat(string value) => new AudioTranslationFormat(value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is AudioTranslationFormat other && Equals(other); + public bool Equals(AudioTranslationFormat other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + public override string ToString() => _value; + } +} diff --git a/src/Generated/Models/AudioTranslationOptions.Serialization.cs b/src/Generated/Models/AudioTranslationOptions.Serialization.cs index 5881d9a4..e2dc1374 100644 --- a/src/Generated/Models/AudioTranslationOptions.Serialization.cs +++ b/src/Generated/Models/AudioTranslationOptions.Serialization.cs @@ -47,7 +47,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReade if (SerializedAdditionalRawData?.ContainsKey("response_format") != true && Optional.IsDefined(ResponseFormat)) { writer.WritePropertyName("response_format"u8); - writer.WriteStringValue(ResponseFormat.Value.ToSerialString()); + writer.WriteStringValue(ResponseFormat.Value.ToString()); } if (SerializedAdditionalRawData?.ContainsKey("temperature") != true && Optional.IsDefined(Temperature)) { @@ -126,7 +126,7 @@ internal static AudioTranslationOptions DeserializeAudioTranslationOptions(JsonE { continue; } - responseFormat = property.Value.GetString().ToAudioTranslationFormat(); + responseFormat = new AudioTranslationFormat(property.Value.GetString()); continue; } if (property.NameEquals("temperature"u8)) @@ -180,7 +180,7 @@ internal virtual MultipartFormDataBinaryContent ToMultipartBinaryBody() } if (Optional.IsDefined(ResponseFormat)) { - content.Add(ResponseFormat.Value.ToSerialString(), "response_format"); + content.Add(ResponseFormat.Value.ToString(), "response_format"); } if (Optional.IsDefined(Temperature)) { diff --git a/src/Generated/Models/ChatMessage.cs b/src/Generated/Models/ChatMessage.cs index 06a9f1ad..507b83b5 100644 --- a/src/Generated/Models/ChatMessage.cs +++ b/src/Generated/Models/ChatMessage.cs @@ -7,7 +7,7 @@ namespace OpenAI.Chat { - public abstract partial class ChatMessage + public partial class ChatMessage { internal IDictionary SerializedAdditionalRawData { get; set; } diff --git a/src/Generated/Models/ChatResponseFormat.cs b/src/Generated/Models/ChatResponseFormat.cs index 54eb0a8b..45de7857 100644 --- a/src/Generated/Models/ChatResponseFormat.cs +++ b/src/Generated/Models/ChatResponseFormat.cs @@ -7,12 +7,9 @@ namespace OpenAI.Chat { - public abstract partial class ChatResponseFormat + public partial class ChatResponseFormat { internal IDictionary SerializedAdditionalRawData { get; set; } - protected ChatResponseFormat() - { - } internal ChatResponseFormat(string type, IDictionary serializedAdditionalRawData) { diff --git a/src/Generated/Models/ChatTool.Serialization.cs b/src/Generated/Models/ChatTool.Serialization.cs index 78fbf981..462a9c65 100644 --- a/src/Generated/Models/ChatTool.Serialization.cs +++ b/src/Generated/Models/ChatTool.Serialization.cs @@ -24,7 +24,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions if (SerializedAdditionalRawData?.ContainsKey("type") != true) { writer.WritePropertyName("type"u8); - writer.WriteStringValue(Kind.ToString()); + writer.WriteStringValue(Kind.ToSerialString()); } if (SerializedAdditionalRawData?.ContainsKey("function") != true) { @@ -81,7 +81,7 @@ internal static ChatTool DeserializeChatTool(JsonElement element, ModelReaderWri { if (property.NameEquals("type"u8)) { - type = new ChatToolKind(property.Value.GetString()); + type = property.Value.GetString().ToChatToolKind(); continue; } if (property.NameEquals("function"u8)) diff --git a/src/Generated/Models/ChatToolCall.Serialization.cs b/src/Generated/Models/ChatToolCall.Serialization.cs index 0b17701a..19fbfe70 100644 --- a/src/Generated/Models/ChatToolCall.Serialization.cs +++ b/src/Generated/Models/ChatToolCall.Serialization.cs @@ -29,7 +29,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOpti if (SerializedAdditionalRawData?.ContainsKey("type") != true) { writer.WritePropertyName("type"u8); - writer.WriteStringValue(Kind.ToString()); + writer.WriteStringValue(Kind.ToSerialString()); } if (SerializedAdditionalRawData?.ContainsKey("function") != true) { @@ -92,7 +92,7 @@ internal static ChatToolCall DeserializeChatToolCall(JsonElement element, ModelR } if (property.NameEquals("type"u8)) { - type = new ChatToolCallKind(property.Value.GetString()); + type = property.Value.GetString().ToChatToolCallKind(); continue; } if (property.NameEquals("function"u8)) diff --git a/src/Generated/Models/ChatToolCallKind.Serialization.cs b/src/Generated/Models/ChatToolCallKind.Serialization.cs new file mode 100644 index 00000000..3e00876f --- /dev/null +++ b/src/Generated/Models/ChatToolCallKind.Serialization.cs @@ -0,0 +1,23 @@ +// + +#nullable disable + +using System; + +namespace OpenAI.Chat +{ + internal static partial class ChatToolCallKindExtensions + { + public static string ToSerialString(this ChatToolCallKind value) => value switch + { + ChatToolCallKind.Function => "function", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatToolCallKind value.") + }; + + public static ChatToolCallKind ToChatToolCallKind(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "function")) return ChatToolCallKind.Function; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatToolCallKind value."); + } + } +} diff --git a/src/Generated/Models/ChatToolCallKind.cs b/src/Generated/Models/ChatToolCallKind.cs deleted file mode 100644 index c75e67ca..00000000 --- a/src/Generated/Models/ChatToolCallKind.cs +++ /dev/null @@ -1,34 +0,0 @@ -// - -#nullable disable - -using System; -using System.ComponentModel; - -namespace OpenAI.Chat -{ - public readonly partial struct ChatToolCallKind : IEquatable - { - private readonly string _value; - - public ChatToolCallKind(string value) - { - _value = value ?? throw new ArgumentNullException(nameof(value)); - } - - private const string FunctionValue = "function"; - - public static ChatToolCallKind Function { get; } = new ChatToolCallKind(FunctionValue); - public static bool operator ==(ChatToolCallKind left, ChatToolCallKind right) => left.Equals(right); - public static bool operator !=(ChatToolCallKind left, ChatToolCallKind right) => !left.Equals(right); - public static implicit operator ChatToolCallKind(string value) => new ChatToolCallKind(value); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => obj is ChatToolCallKind other && Equals(other); - public bool Equals(ChatToolCallKind other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; - public override string ToString() => _value; - } -} diff --git a/src/Generated/Models/ChatToolKind.Serialization.cs b/src/Generated/Models/ChatToolKind.Serialization.cs new file mode 100644 index 00000000..0a04ab83 --- /dev/null +++ b/src/Generated/Models/ChatToolKind.Serialization.cs @@ -0,0 +1,23 @@ +// + +#nullable disable + +using System; + +namespace OpenAI.Chat +{ + internal static partial class ChatToolKindExtensions + { + public static string ToSerialString(this ChatToolKind value) => value switch + { + ChatToolKind.Function => "function", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatToolKind value.") + }; + + public static ChatToolKind ToChatToolKind(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "function")) return ChatToolKind.Function; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ChatToolKind value."); + } + } +} diff --git a/src/Generated/Models/ChatToolKind.cs b/src/Generated/Models/ChatToolKind.cs deleted file mode 100644 index 793f2f7a..00000000 --- a/src/Generated/Models/ChatToolKind.cs +++ /dev/null @@ -1,34 +0,0 @@ -// - -#nullable disable - -using System; -using System.ComponentModel; - -namespace OpenAI.Chat -{ - public readonly partial struct ChatToolKind : IEquatable - { - private readonly string _value; - - public ChatToolKind(string value) - { - _value = value ?? throw new ArgumentNullException(nameof(value)); - } - - private const string FunctionValue = "function"; - - public static ChatToolKind Function { get; } = new ChatToolKind(FunctionValue); - public static bool operator ==(ChatToolKind left, ChatToolKind right) => left.Equals(right); - public static bool operator !=(ChatToolKind left, ChatToolKind right) => !left.Equals(right); - public static implicit operator ChatToolKind(string value) => new ChatToolKind(value); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => obj is ChatToolKind other && Equals(other); - public bool Equals(ChatToolKind other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; - public override string ToString() => _value; - } -} diff --git a/src/Generated/Models/FilePurpose.Serialization.cs b/src/Generated/Models/FilePurpose.Serialization.cs new file mode 100644 index 00000000..00250272 --- /dev/null +++ b/src/Generated/Models/FilePurpose.Serialization.cs @@ -0,0 +1,35 @@ +// + +#nullable disable + +using System; + +namespace OpenAI.Files +{ + internal static partial class FilePurposeExtensions + { + public static string ToSerialString(this FilePurpose value) => value switch + { + FilePurpose.Assistants => "assistants", + FilePurpose.AssistantsOutput => "assistants_output", + FilePurpose.Batch => "batch", + FilePurpose.BatchOutput => "batch_output", + FilePurpose.FineTune => "fine-tune", + FilePurpose.FineTuneResults => "fine-tune-results", + FilePurpose.Vision => "vision", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FilePurpose value.") + }; + + public static FilePurpose ToFilePurpose(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "assistants")) return FilePurpose.Assistants; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "assistants_output")) return FilePurpose.AssistantsOutput; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "batch")) return FilePurpose.Batch; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "batch_output")) return FilePurpose.BatchOutput; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "fine-tune")) return FilePurpose.FineTune; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "fine-tune-results")) return FilePurpose.FineTuneResults; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "vision")) return FilePurpose.Vision; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FilePurpose value."); + } + } +} diff --git a/src/Generated/Models/FileStatus.Serialization.cs b/src/Generated/Models/FileStatus.Serialization.cs new file mode 100644 index 00000000..cee9bafe --- /dev/null +++ b/src/Generated/Models/FileStatus.Serialization.cs @@ -0,0 +1,27 @@ +// + +#nullable disable + +using System; + +namespace OpenAI.Files +{ + internal static partial class FileStatusExtensions + { + public static string ToSerialString(this FileStatus value) => value switch + { + FileStatus.Uploaded => "uploaded", + FileStatus.Processed => "processed", + FileStatus.Error => "error", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FileStatus value.") + }; + + public static FileStatus ToFileStatus(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "uploaded")) return FileStatus.Uploaded; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "processed")) return FileStatus.Processed; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "error")) return FileStatus.Error; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FileStatus value."); + } + } +} diff --git a/src/Generated/Models/GeneratedImageFormat.Serialization.cs b/src/Generated/Models/GeneratedImageFormat.Serialization.cs deleted file mode 100644 index 6cbd234d..00000000 --- a/src/Generated/Models/GeneratedImageFormat.Serialization.cs +++ /dev/null @@ -1,25 +0,0 @@ -// - -#nullable disable - -using System; - -namespace OpenAI.Images -{ - internal static partial class GeneratedImageFormatExtensions - { - public static string ToSerialString(this GeneratedImageFormat value) => value switch - { - GeneratedImageFormat.Uri => "url", - GeneratedImageFormat.Bytes => "b64_json", - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageFormat value.") - }; - - public static GeneratedImageFormat ToGeneratedImageFormat(this string value) - { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "url")) return GeneratedImageFormat.Uri; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "b64_json")) return GeneratedImageFormat.Bytes; - throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageFormat value."); - } - } -} diff --git a/src/Generated/Models/GeneratedImageFormat.cs b/src/Generated/Models/GeneratedImageFormat.cs new file mode 100644 index 00000000..ff4ede1c --- /dev/null +++ b/src/Generated/Models/GeneratedImageFormat.cs @@ -0,0 +1,33 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace OpenAI.Images +{ + public readonly partial struct GeneratedImageFormat : IEquatable + { + private readonly string _value; + + public GeneratedImageFormat(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string UriValue = "url"; + private const string BytesValue = "b64_json"; + public static bool operator ==(GeneratedImageFormat left, GeneratedImageFormat right) => left.Equals(right); + public static bool operator !=(GeneratedImageFormat left, GeneratedImageFormat right) => !left.Equals(right); + public static implicit operator GeneratedImageFormat(string value) => new GeneratedImageFormat(value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is GeneratedImageFormat other && Equals(other); + public bool Equals(GeneratedImageFormat other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + public override string ToString() => _value; + } +} diff --git a/src/Generated/Models/GeneratedImageQuality.Serialization.cs b/src/Generated/Models/GeneratedImageQuality.Serialization.cs deleted file mode 100644 index 96d00a50..00000000 --- a/src/Generated/Models/GeneratedImageQuality.Serialization.cs +++ /dev/null @@ -1,25 +0,0 @@ -// - -#nullable disable - -using System; - -namespace OpenAI.Images -{ - internal static partial class GeneratedImageQualityExtensions - { - public static string ToSerialString(this GeneratedImageQuality value) => value switch - { - GeneratedImageQuality.Standard => "standard", - GeneratedImageQuality.High => "hd", - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageQuality value.") - }; - - public static GeneratedImageQuality ToGeneratedImageQuality(this string value) - { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "standard")) return GeneratedImageQuality.Standard; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "hd")) return GeneratedImageQuality.High; - throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageQuality value."); - } - } -} diff --git a/src/Generated/Models/GeneratedImageQuality.cs b/src/Generated/Models/GeneratedImageQuality.cs new file mode 100644 index 00000000..fb53a985 --- /dev/null +++ b/src/Generated/Models/GeneratedImageQuality.cs @@ -0,0 +1,35 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace OpenAI.Images +{ + public readonly partial struct GeneratedImageQuality : IEquatable + { + private readonly string _value; + + public GeneratedImageQuality(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string StandardValue = "standard"; + private const string HighValue = "hd"; + + public static GeneratedImageQuality Standard { get; } = new GeneratedImageQuality(StandardValue); + public static bool operator ==(GeneratedImageQuality left, GeneratedImageQuality right) => left.Equals(right); + public static bool operator !=(GeneratedImageQuality left, GeneratedImageQuality right) => !left.Equals(right); + public static implicit operator GeneratedImageQuality(string value) => new GeneratedImageQuality(value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is GeneratedImageQuality other && Equals(other); + public bool Equals(GeneratedImageQuality other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + public override string ToString() => _value; + } +} diff --git a/src/Generated/Models/GeneratedImageStyle.Serialization.cs b/src/Generated/Models/GeneratedImageStyle.Serialization.cs deleted file mode 100644 index ebb7cbd7..00000000 --- a/src/Generated/Models/GeneratedImageStyle.Serialization.cs +++ /dev/null @@ -1,25 +0,0 @@ -// - -#nullable disable - -using System; - -namespace OpenAI.Images -{ - internal static partial class GeneratedImageStyleExtensions - { - public static string ToSerialString(this GeneratedImageStyle value) => value switch - { - GeneratedImageStyle.Vivid => "vivid", - GeneratedImageStyle.Natural => "natural", - _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageStyle value.") - }; - - public static GeneratedImageStyle ToGeneratedImageStyle(this string value) - { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "vivid")) return GeneratedImageStyle.Vivid; - if (StringComparer.OrdinalIgnoreCase.Equals(value, "natural")) return GeneratedImageStyle.Natural; - throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown GeneratedImageStyle value."); - } - } -} diff --git a/src/Generated/Models/GeneratedImageStyle.cs b/src/Generated/Models/GeneratedImageStyle.cs new file mode 100644 index 00000000..5a68da2b --- /dev/null +++ b/src/Generated/Models/GeneratedImageStyle.cs @@ -0,0 +1,36 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace OpenAI.Images +{ + public readonly partial struct GeneratedImageStyle : IEquatable + { + private readonly string _value; + + public GeneratedImageStyle(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string VividValue = "vivid"; + private const string NaturalValue = "natural"; + + public static GeneratedImageStyle Vivid { get; } = new GeneratedImageStyle(VividValue); + public static GeneratedImageStyle Natural { get; } = new GeneratedImageStyle(NaturalValue); + public static bool operator ==(GeneratedImageStyle left, GeneratedImageStyle right) => left.Equals(right); + public static bool operator !=(GeneratedImageStyle left, GeneratedImageStyle right) => !left.Equals(right); + public static implicit operator GeneratedImageStyle(string value) => new GeneratedImageStyle(value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is GeneratedImageStyle other && Equals(other); + public bool Equals(GeneratedImageStyle other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + public override string ToString() => _value; + } +} diff --git a/src/Generated/Models/ImageEditOptions.Serialization.cs b/src/Generated/Models/ImageEditOptions.Serialization.cs index 45e61401..0595ecc6 100644 --- a/src/Generated/Models/ImageEditOptions.Serialization.cs +++ b/src/Generated/Models/ImageEditOptions.Serialization.cs @@ -92,7 +92,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriter if (ResponseFormat != null) { writer.WritePropertyName("response_format"u8); - writer.WriteStringValue(ResponseFormat.Value.ToSerialString()); + writer.WriteStringValue(ResponseFormat.Value.ToString()); } else { @@ -214,7 +214,7 @@ internal static ImageEditOptions DeserializeImageEditOptions(JsonElement element responseFormat = null; continue; } - responseFormat = property.Value.GetString().ToGeneratedImageFormat(); + responseFormat = new GeneratedImageFormat(property.Value.GetString()); continue; } if (property.NameEquals("user"u8)) @@ -290,7 +290,7 @@ internal virtual MultipartFormDataBinaryContent ToMultipartBinaryBody() { if (ResponseFormat != null) { - content.Add(ResponseFormat.Value.ToSerialString(), "response_format"); + content.Add(ResponseFormat.Value.ToString(), "response_format"); } } if (Optional.IsDefined(EndUserId)) diff --git a/src/Generated/Models/ImageGenerationOptions.Serialization.cs b/src/Generated/Models/ImageGenerationOptions.Serialization.cs index a007fd04..386ee3b6 100644 --- a/src/Generated/Models/ImageGenerationOptions.Serialization.cs +++ b/src/Generated/Models/ImageGenerationOptions.Serialization.cs @@ -53,14 +53,14 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReader if (SerializedAdditionalRawData?.ContainsKey("quality") != true && Optional.IsDefined(Quality)) { writer.WritePropertyName("quality"u8); - writer.WriteStringValue(Quality.Value.ToSerialString()); + writer.WriteStringValue(Quality.Value.ToString()); } if (SerializedAdditionalRawData?.ContainsKey("response_format") != true && Optional.IsDefined(ResponseFormat)) { if (ResponseFormat != null) { writer.WritePropertyName("response_format"u8); - writer.WriteStringValue(ResponseFormat.Value.ToSerialString()); + writer.WriteStringValue(ResponseFormat.Value.ToString()); } else { @@ -84,7 +84,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReader if (Style != null) { writer.WritePropertyName("style"u8); - writer.WriteStringValue(Style.Value.ToSerialString()); + writer.WriteStringValue(Style.Value.ToString()); } else { @@ -181,7 +181,7 @@ internal static ImageGenerationOptions DeserializeImageGenerationOptions(JsonEle { continue; } - quality = property.Value.GetString().ToGeneratedImageQuality(); + quality = new GeneratedImageQuality(property.Value.GetString()); continue; } if (property.NameEquals("response_format"u8)) @@ -191,7 +191,7 @@ internal static ImageGenerationOptions DeserializeImageGenerationOptions(JsonEle responseFormat = null; continue; } - responseFormat = property.Value.GetString().ToGeneratedImageFormat(); + responseFormat = new GeneratedImageFormat(property.Value.GetString()); continue; } if (property.NameEquals("size"u8)) @@ -211,7 +211,7 @@ internal static ImageGenerationOptions DeserializeImageGenerationOptions(JsonEle style = null; continue; } - style = property.Value.GetString().ToGeneratedImageStyle(); + style = new GeneratedImageStyle(property.Value.GetString()); continue; } if (property.NameEquals("user"u8)) diff --git a/src/Generated/Models/ImageVariationOptions.Serialization.cs b/src/Generated/Models/ImageVariationOptions.Serialization.cs index aea32c3d..38d3c075 100644 --- a/src/Generated/Models/ImageVariationOptions.Serialization.cs +++ b/src/Generated/Models/ImageVariationOptions.Serialization.cs @@ -63,7 +63,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderW if (ResponseFormat != null) { writer.WritePropertyName("response_format"u8); - writer.WriteStringValue(ResponseFormat.Value.ToSerialString()); + writer.WriteStringValue(ResponseFormat.Value.ToString()); } else { @@ -171,7 +171,7 @@ internal static ImageVariationOptions DeserializeImageVariationOptions(JsonEleme responseFormat = null; continue; } - responseFormat = property.Value.GetString().ToGeneratedImageFormat(); + responseFormat = new GeneratedImageFormat(property.Value.GetString()); continue; } if (property.NameEquals("size"u8)) @@ -243,7 +243,7 @@ internal virtual MultipartFormDataBinaryContent ToMultipartBinaryBody() { if (ResponseFormat != null) { - content.Add(ResponseFormat.Value.ToSerialString(), "response_format"); + content.Add(ResponseFormat.Value.ToString(), "response_format"); } } if (Optional.IsDefined(Size)) diff --git a/src/Generated/Models/OpenAIFile.Serialization.cs b/src/Generated/Models/OpenAIFile.Serialization.cs index ae2e64ee..0e3815cd 100644 --- a/src/Generated/Models/OpenAIFile.Serialization.cs +++ b/src/Generated/Models/OpenAIFile.Serialization.cs @@ -56,12 +56,12 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOption if (SerializedAdditionalRawData?.ContainsKey("purpose") != true) { writer.WritePropertyName("purpose"u8); - writer.WriteStringValue(Purpose.ToString()); + writer.WriteStringValue(Purpose.ToSerialString()); } if (SerializedAdditionalRawData?.ContainsKey("status") != true) { writer.WritePropertyName("status"u8); - writer.WriteStringValue(Status.ToString()); + writer.WriteStringValue(Status.ToSerialString()); } if (SerializedAdditionalRawData?.ContainsKey("status_details") != true && Optional.IsDefined(StatusDetails)) { @@ -115,8 +115,8 @@ internal static OpenAIFile DeserializeOpenAIFile(JsonElement element, ModelReade DateTimeOffset createdAt = default; string filename = default; InternalOpenAIFileObject @object = default; - OpenAIFilePurpose purpose = default; - OpenAIFileStatus status = default; + FilePurpose purpose = default; + FileStatus status = default; string statusDetails = default; IDictionary serializedAdditionalRawData = default; Dictionary rawDataDictionary = new Dictionary(); @@ -154,12 +154,12 @@ internal static OpenAIFile DeserializeOpenAIFile(JsonElement element, ModelReade } if (property.NameEquals("purpose"u8)) { - purpose = new OpenAIFilePurpose(property.Value.GetString()); + purpose = property.Value.GetString().ToFilePurpose(); continue; } if (property.NameEquals("status"u8)) { - status = new OpenAIFileStatus(property.Value.GetString()); + status = property.Value.GetString().ToFileStatus(); continue; } if (property.NameEquals("status_details"u8)) diff --git a/src/Generated/Models/OpenAIFile.cs b/src/Generated/Models/OpenAIFile.cs index 1176232c..23577b77 100644 --- a/src/Generated/Models/OpenAIFile.cs +++ b/src/Generated/Models/OpenAIFile.cs @@ -10,7 +10,7 @@ namespace OpenAI.Files public partial class OpenAIFile { internal IDictionary SerializedAdditionalRawData { get; set; } - internal OpenAIFile(string id, int? sizeInBytes, DateTimeOffset createdAt, string filename, OpenAIFilePurpose purpose, OpenAIFileStatus status) + internal OpenAIFile(string id, int? sizeInBytes, DateTimeOffset createdAt, string filename, FilePurpose purpose, FileStatus status) { Argument.AssertNotNull(id, nameof(id)); Argument.AssertNotNull(filename, nameof(filename)); @@ -23,7 +23,7 @@ internal OpenAIFile(string id, int? sizeInBytes, DateTimeOffset createdAt, strin Status = status; } - internal OpenAIFile(string id, int? sizeInBytes, DateTimeOffset createdAt, string filename, InternalOpenAIFileObject @object, OpenAIFilePurpose purpose, OpenAIFileStatus status, string statusDetails, IDictionary serializedAdditionalRawData) + internal OpenAIFile(string id, int? sizeInBytes, DateTimeOffset createdAt, string filename, InternalOpenAIFileObject @object, FilePurpose purpose, FileStatus status, string statusDetails, IDictionary serializedAdditionalRawData) { Id = id; SizeInBytes = sizeInBytes; @@ -44,6 +44,6 @@ internal OpenAIFile() public DateTimeOffset CreatedAt { get; } public string Filename { get; } - public OpenAIFilePurpose Purpose { get; } + public FilePurpose Purpose { get; } } } diff --git a/src/Generated/Models/OpenAIFilePurpose.cs b/src/Generated/Models/OpenAIFilePurpose.cs deleted file mode 100644 index 71c6cfbf..00000000 --- a/src/Generated/Models/OpenAIFilePurpose.cs +++ /dev/null @@ -1,46 +0,0 @@ -// - -#nullable disable - -using System; -using System.ComponentModel; - -namespace OpenAI.Files -{ - public readonly partial struct OpenAIFilePurpose : IEquatable - { - private readonly string _value; - - public OpenAIFilePurpose(string value) - { - _value = value ?? throw new ArgumentNullException(nameof(value)); - } - - private const string AssistantsValue = "assistants"; - private const string AssistantsOutputValue = "assistants_output"; - private const string BatchValue = "batch"; - private const string BatchOutputValue = "batch_output"; - private const string FineTuneValue = "fine-tune"; - private const string FineTuneResultsValue = "fine-tune-results"; - private const string VisionValue = "vision"; - - public static OpenAIFilePurpose Assistants { get; } = new OpenAIFilePurpose(AssistantsValue); - public static OpenAIFilePurpose AssistantsOutput { get; } = new OpenAIFilePurpose(AssistantsOutputValue); - public static OpenAIFilePurpose Batch { get; } = new OpenAIFilePurpose(BatchValue); - public static OpenAIFilePurpose BatchOutput { get; } = new OpenAIFilePurpose(BatchOutputValue); - public static OpenAIFilePurpose FineTune { get; } = new OpenAIFilePurpose(FineTuneValue); - public static OpenAIFilePurpose FineTuneResults { get; } = new OpenAIFilePurpose(FineTuneResultsValue); - public static OpenAIFilePurpose Vision { get; } = new OpenAIFilePurpose(VisionValue); - public static bool operator ==(OpenAIFilePurpose left, OpenAIFilePurpose right) => left.Equals(right); - public static bool operator !=(OpenAIFilePurpose left, OpenAIFilePurpose right) => !left.Equals(right); - public static implicit operator OpenAIFilePurpose(string value) => new OpenAIFilePurpose(value); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => obj is OpenAIFilePurpose other && Equals(other); - public bool Equals(OpenAIFilePurpose other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; - public override string ToString() => _value; - } -} diff --git a/src/Generated/Models/OpenAIFileStatus.cs b/src/Generated/Models/OpenAIFileStatus.cs deleted file mode 100644 index 9176c861..00000000 --- a/src/Generated/Models/OpenAIFileStatus.cs +++ /dev/null @@ -1,38 +0,0 @@ -// - -#nullable disable - -using System; -using System.ComponentModel; - -namespace OpenAI.Files -{ - public readonly partial struct OpenAIFileStatus : IEquatable - { - private readonly string _value; - - public OpenAIFileStatus(string value) - { - _value = value ?? throw new ArgumentNullException(nameof(value)); - } - - private const string UploadedValue = "uploaded"; - private const string ProcessedValue = "processed"; - private const string ErrorValue = "error"; - - public static OpenAIFileStatus Uploaded { get; } = new OpenAIFileStatus(UploadedValue); - public static OpenAIFileStatus Processed { get; } = new OpenAIFileStatus(ProcessedValue); - public static OpenAIFileStatus Error { get; } = new OpenAIFileStatus(ErrorValue); - public static bool operator ==(OpenAIFileStatus left, OpenAIFileStatus right) => left.Equals(right); - public static bool operator !=(OpenAIFileStatus left, OpenAIFileStatus right) => !left.Equals(right); - public static implicit operator OpenAIFileStatus(string value) => new OpenAIFileStatus(value); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => obj is OpenAIFileStatus other && Equals(other); - public bool Equals(OpenAIFileStatus other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; - public override string ToString() => _value; - } -} diff --git a/src/Generated/Models/StreamingChatToolCallUpdate.Serialization.cs b/src/Generated/Models/StreamingChatToolCallUpdate.Serialization.cs index 123891d2..8d0c9b48 100644 --- a/src/Generated/Models/StreamingChatToolCallUpdate.Serialization.cs +++ b/src/Generated/Models/StreamingChatToolCallUpdate.Serialization.cs @@ -34,7 +34,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR if (SerializedAdditionalRawData?.ContainsKey("type") != true) { writer.WritePropertyName("type"u8); - writer.WriteStringValue(Kind.ToString()); + writer.WriteStringValue(Kind.ToSerialString()); } if (SerializedAdditionalRawData?.ContainsKey("function") != true && Optional.IsDefined(Function)) { @@ -107,7 +107,7 @@ internal static StreamingChatToolCallUpdate DeserializeStreamingChatToolCallUpda { continue; } - type = new ChatToolCallKind(property.Value.GetString()); + type = property.Value.GetString().ToChatToolCallKind(); continue; } if (property.NameEquals("function"u8)) diff --git a/src/Generated/Models/TranscribedSegment.Serialization.cs b/src/Generated/Models/TranscribedSegment.Serialization.cs index 71025c45..6af68a9e 100644 --- a/src/Generated/Models/TranscribedSegment.Serialization.cs +++ b/src/Generated/Models/TranscribedSegment.Serialization.cs @@ -50,7 +50,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWrit { writer.WritePropertyName("tokens"u8); writer.WriteStartArray(); - foreach (var item in TokenIds) + foreach (var item in TokenIds.Span) { writer.WriteNumberValue(item); } @@ -123,7 +123,7 @@ internal static TranscribedSegment DeserializeTranscribedSegment(JsonElement ele TimeSpan start = default; TimeSpan end = default; string text = default; - IReadOnlyList tokens = default; + ReadOnlyMemory tokens = default; float temperature = default; float avgLogprob = default; float compressionRatio = default; @@ -159,12 +159,18 @@ internal static TranscribedSegment DeserializeTranscribedSegment(JsonElement ele } if (property.NameEquals("tokens"u8)) { - List array = new List(); + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + int index = 0; + int[] array = new int[property.Value.GetArrayLength()]; foreach (var item in property.Value.EnumerateArray()) { - array.Add(item.GetInt32()); + array[index] = item.GetInt32(); + index++; } - tokens = array; + tokens = new ReadOnlyMemory(array); continue; } if (property.NameEquals("temperature"u8)) diff --git a/src/Generated/Models/TranscribedSegment.cs b/src/Generated/Models/TranscribedSegment.cs index 5f27659f..fb245f40 100644 --- a/src/Generated/Models/TranscribedSegment.cs +++ b/src/Generated/Models/TranscribedSegment.cs @@ -4,30 +4,28 @@ using System; using System.Collections.Generic; -using System.Linq; namespace OpenAI.Audio { public readonly partial struct TranscribedSegment { - internal TranscribedSegment(int id, int seekOffset, TimeSpan startTime, TimeSpan endTime, string text, IEnumerable tokenIds, float temperature, float averageLogProbability, float compressionRatio, float noSpeechProbability) + internal TranscribedSegment(int id, int seekOffset, TimeSpan startTime, TimeSpan endTime, string text, ReadOnlyMemory tokenIds, float temperature, float averageLogProbability, float compressionRatio, float noSpeechProbability) { Argument.AssertNotNull(text, nameof(text)); - Argument.AssertNotNull(tokenIds, nameof(tokenIds)); Id = id; SeekOffset = seekOffset; StartTime = startTime; EndTime = endTime; Text = text; - TokenIds = tokenIds.ToList(); + TokenIds = tokenIds; Temperature = temperature; AverageLogProbability = averageLogProbability; CompressionRatio = compressionRatio; NoSpeechProbability = noSpeechProbability; } - internal TranscribedSegment(int id, int seekOffset, TimeSpan startTime, TimeSpan endTime, string text, IReadOnlyList tokenIds, float temperature, float averageLogProbability, float compressionRatio, float noSpeechProbability, IDictionary serializedAdditionalRawData) + internal TranscribedSegment(int id, int seekOffset, TimeSpan startTime, TimeSpan endTime, string text, ReadOnlyMemory tokenIds, float temperature, float averageLogProbability, float compressionRatio, float noSpeechProbability, IDictionary serializedAdditionalRawData) { Id = id; SeekOffset = seekOffset; diff --git a/src/Generated/OpenAIModelFactory.cs b/src/Generated/OpenAIModelFactory.cs index 2e0e0aaf..36af3252 100644 --- a/src/Generated/OpenAIModelFactory.cs +++ b/src/Generated/OpenAIModelFactory.cs @@ -133,17 +133,15 @@ public static TranscribedWord TranscribedWord(string word = null, TimeSpan start return new TranscribedWord(word, startTime, endTime, serializedAdditionalRawData: null); } - public static TranscribedSegment TranscribedSegment(int id = default, int seekOffset = default, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, IEnumerable tokenIds = null, float temperature = default, float averageLogProbability = default, float compressionRatio = default, float noSpeechProbability = default) + public static TranscribedSegment TranscribedSegment(int id = default, int seekOffset = default, TimeSpan startTime = default, TimeSpan endTime = default, string text = null, ReadOnlyMemory tokenIds = default, float temperature = default, float averageLogProbability = default, float compressionRatio = default, float noSpeechProbability = default) { - tokenIds ??= new List(); - return new TranscribedSegment( id, seekOffset, startTime, endTime, text, - tokenIds?.ToList(), + tokenIds, temperature, averageLogProbability, compressionRatio, diff --git a/src/Generated/VectorStoreClient.cs b/src/Generated/VectorStoreClient.cs index 2d597f0c..520c076a 100644 --- a/src/Generated/VectorStoreClient.cs +++ b/src/Generated/VectorStoreClient.cs @@ -22,289 +22,20 @@ protected VectorStoreClient() { } - internal PipelineMessage CreateGetVectorStoresRequest(int? limit, string order, string after, string before, RequestOptions options) + public virtual async Task CreateVectorStoreAsync(BinaryContent content, RequestOptions options = null) { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores", false); - if (limit != null) - { - uri.AppendQuery("limit", limit.Value, true); - } - if (order != null) - { - uri.AppendQuery("order", order, true); - } - if (after != null) - { - uri.AppendQuery("after", after, true); - } - if (before != null) - { - uri.AppendQuery("before", before, true); - } - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateCreateVectorStoreRequest(BinaryContent content, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } - - internal PipelineMessage CreateGetVectorStoreRequest(string vectorStoreId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateModifyVectorStoreRequest(string vectorStoreId, BinaryContent content, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } - - internal PipelineMessage CreateDeleteVectorStoreRequest(string vectorStoreId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "DELETE"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } + Argument.AssertNotNull(content, nameof(content)); - internal PipelineMessage CreateGetVectorStoreFilesRequest(string vectorStoreId, int? limit, string order, string after, string before, string filter, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/files", false); - if (limit != null) - { - uri.AppendQuery("limit", limit.Value, true); - } - if (order != null) - { - uri.AppendQuery("order", order, true); - } - if (after != null) - { - uri.AppendQuery("after", after, true); - } - if (before != null) - { - uri.AppendQuery("before", before, true); - } - if (filter != null) - { - uri.AppendQuery("filter", filter, true); - } - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; + using PipelineMessage message = CreateCreateVectorStoreRequest(content, options); + return ClientResult.FromResponse(await _pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } - internal PipelineMessage CreateCreateVectorStoreFileRequest(string vectorStoreId, BinaryContent content, RequestOptions options) + public virtual ClientResult CreateVectorStore(BinaryContent content, RequestOptions options = null) { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/files", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } + Argument.AssertNotNull(content, nameof(content)); - internal PipelineMessage CreateGetVectorStoreFileRequest(string vectorStoreId, string fileId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/files/", false); - uri.AppendPath(fileId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateDeleteVectorStoreFileRequest(string vectorStoreId, string fileId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "DELETE"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/files/", false); - uri.AppendPath(fileId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateCreateVectorStoreFileBatchRequest(string vectorStoreId, BinaryContent content, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/file_batches", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - request.Headers.Set("Content-Type", "application/json"); - request.Content = content; - message.Apply(options); - return message; - } - - internal PipelineMessage CreateGetVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/file_batches/", false); - uri.AppendPath(batchId, true); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateCancelVectorStoreFileBatchRequest(string vectorStoreId, string batchId, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "POST"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/file_batches/", false); - uri.AppendPath(batchId, true); - uri.AppendPath("/cancel", false); - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; - } - - internal PipelineMessage CreateGetFilesInVectorStoreBatchesRequest(string vectorStoreId, string batchId, int? limit, string order, string after, string before, string filter, RequestOptions options) - { - var message = _pipeline.CreateMessage(); - message.ResponseClassifier = PipelineMessageClassifier200; - var request = message.Request; - request.Method = "GET"; - var uri = new ClientUriBuilder(); - uri.Reset(_endpoint); - uri.AppendPath("/vector_stores/", false); - uri.AppendPath(vectorStoreId, true); - uri.AppendPath("/file_batches/", false); - uri.AppendPath(batchId, true); - uri.AppendPath("/files", false); - if (limit != null) - { - uri.AppendQuery("limit", limit.Value, true); - } - if (order != null) - { - uri.AppendQuery("order", order, true); - } - if (after != null) - { - uri.AppendQuery("after", after, true); - } - if (before != null) - { - uri.AppendQuery("before", before, true); - } - if (filter != null) - { - uri.AppendQuery("filter", filter, true); - } - request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); - message.Apply(options); - return message; + using PipelineMessage message = CreateCreateVectorStoreRequest(content, options); + return ClientResult.FromResponse(_pipeline.ProcessMessage(message, options)); } private static PipelineMessageClassifier _pipelineMessageClassifier200; diff --git a/tests/Assistants/Assistants.VectorStoresTests.cs b/tests/Assistants/Assistants.VectorStoresTests.cs index 126b6978..9f88f443 100644 --- a/tests/Assistants/Assistants.VectorStoresTests.cs +++ b/tests/Assistants/Assistants.VectorStoresTests.cs @@ -21,7 +21,7 @@ namespace OpenAI.Tests.VectorStores; [Category("Assistants")] public class VectorStoresTests : SyncAsyncTestBase { - private readonly List _jobsToCancel = []; + private readonly List _jobsToCancel = []; private readonly List _associationsToRemove = []; private readonly List _filesToDelete = []; private readonly List _vectorStoresToDelete = []; @@ -40,13 +40,15 @@ public async Task CanCreateGetAndDeleteVectorStores() { VectorStoreClient client = GetTestClient(); - VectorStore vectorStore = IsAsync - ? await client.CreateVectorStoreAsync() - : client.CreateVectorStore(); + CreateVectorStoreOperation createOperation = IsAsync + ? await client.CreateVectorStoreAsync(waitUntilCompleted: false) + : client.CreateVectorStore(waitUntilCompleted: false); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); + VectorStoreDeletionResult deletionResult = IsAsync - ? await client.DeleteVectorStoreAsync(vectorStore) - : client.DeleteVectorStore(vectorStore); + ? await client.DeleteVectorStoreAsync(vectorStore.Id) + : client.DeleteVectorStore(vectorStore.Id); Assert.That(deletionResult.VectorStoreId, Is.EqualTo(vectorStore.Id)); Assert.That(deletionResult.Deleted, Is.True); _vectorStoresToDelete.RemoveAt(_vectorStoresToDelete.Count - 1); @@ -66,25 +68,24 @@ public async Task CanCreateGetAndDeleteVectorStores() ["test-key"] = "test-value", }, }; + createOperation = IsAsync + ? await client.CreateVectorStoreAsync(waitUntilCompleted: false, creationOptions) + : client.CreateVectorStore(waitUntilCompleted: false, creationOptions); - vectorStore = IsAsync - ? await client.CreateVectorStoreAsync(creationOptions) - : client.CreateVectorStore(creationOptions); - Validate(vectorStore); + Validate(createOperation.Value); Assert.Multiple(() => { - Assert.That(vectorStore.Name, Is.EqualTo("test vector store")); - Assert.That(vectorStore.ExpirationPolicy?.Anchor, Is.EqualTo(VectorStoreExpirationAnchor.LastActiveAt)); - Assert.That(vectorStore.ExpirationPolicy?.Days, Is.EqualTo(3)); - Assert.That(vectorStore.FileCounts.Total, Is.EqualTo(1)); - Assert.That(vectorStore.CreatedAt, Is.GreaterThan(s_2024)); - Assert.That(vectorStore.ExpiresAt, Is.GreaterThan(s_2024)); - Assert.That(vectorStore.Status, Is.EqualTo(VectorStoreStatus.InProgress)); - Assert.That(vectorStore.Metadata?.TryGetValue("test-key", out string metadataValue) == true && metadataValue == "test-value"); + Assert.That(createOperation.Value.Name, Is.EqualTo("test vector store")); + Assert.That(createOperation.Value.ExpirationPolicy?.Anchor, Is.EqualTo(VectorStoreExpirationAnchor.LastActiveAt)); + Assert.That(createOperation.Value.ExpirationPolicy?.Days, Is.EqualTo(3)); + Assert.That(createOperation.Value.FileCounts.Total, Is.EqualTo(1)); + Assert.That(createOperation.Value.CreatedAt, Is.GreaterThan(s_2024)); + Assert.That(createOperation.Value.ExpiresAt, Is.GreaterThan(s_2024)); + Assert.That(createOperation.Status, Is.EqualTo(VectorStoreStatus.InProgress)); + Assert.That(createOperation.Value.Metadata?.TryGetValue("test-key", out string metadataValue) == true && metadataValue == "test-value"); }); - vectorStore = IsAsync - ? await client.GetVectorStoreAsync(vectorStore) - : client.GetVectorStore(vectorStore); + + vectorStore = createOperation.Value; Assert.Multiple(() => { Assert.That(vectorStore.Name, Is.EqualTo("test vector store")); @@ -108,9 +109,10 @@ public async Task CanCreateGetAndDeleteVectorStores() { creationOptions.FileIds.Add(file.Id); } - vectorStore = IsAsync - ? await client.CreateVectorStoreAsync(creationOptions) - : client.CreateVectorStore(creationOptions); + createOperation = IsAsync + ? await client.CreateVectorStoreAsync(waitUntilCompleted: true, creationOptions) + : client.CreateVectorStore(waitUntilCompleted: true, creationOptions); + vectorStore = createOperation.Value; Validate(vectorStore); Assert.Multiple(() => @@ -128,10 +130,11 @@ public void CanEnumerateVectorStores() VectorStoreClient client = GetTestClient(); for (int i = 0; i < 10; i++) { - VectorStore vectorStore = client.CreateVectorStore(new VectorStoreCreationOptions() + CreateVectorStoreOperation createOperation = client.CreateVectorStore(waitUntilCompleted: true, new VectorStoreCreationOptions() { Name = $"Test Vector Store {i}", }); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); Assert.That(vectorStore.Name, Is.EqualTo($"Test Vector Store {i}")); } @@ -167,11 +170,14 @@ public async Task CanEnumerateVectorStoresAsync() VectorStoreClient client = GetTestClient(); for (int i = 0; i < 10; i++) { - VectorStore vectorStore = await client.CreateVectorStoreAsync(new VectorStoreCreationOptions() - { - Name = $"Test Vector Store {i}", - }); + CreateVectorStoreOperation createOperation = await client.CreateVectorStoreAsync(waitUntilCompleted: true, + new VectorStoreCreationOptions() + { + Name = $"Test Vector Store {i}", + }); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); + Assert.That(vectorStore.Name, Is.EqualTo($"Test Vector Store {i}")); } @@ -202,16 +208,20 @@ public async Task CanEnumerateVectorStoresAsync() public async Task CanAssociateFiles() { VectorStoreClient client = GetTestClient(); - VectorStore vectorStore = client.CreateVectorStore(); + CreateVectorStoreOperation createOperation = IsAsync + ? await client.CreateVectorStoreAsync(waitUntilCompleted: true) + : client.CreateVectorStore(waitUntilCompleted: true); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); IReadOnlyList files = GetNewTestFiles(3); foreach (OpenAIFile file in files) { - VectorStoreFileAssociation association = IsAsync - ? await client.AddFileToVectorStoreAsync(vectorStore, file) - : client.AddFileToVectorStore(vectorStore, file); + AddFileToVectorStoreOperation addOperation = IsAsync + ? await client.AddFileToVectorStoreAsync(vectorStore.Id, file.Id, waitUntilCompleted: false) + : client.AddFileToVectorStore(vectorStore.Id, file.Id, waitUntilCompleted: false); + VectorStoreFileAssociation association = addOperation.Value; Validate(association); Assert.Multiple(() => { @@ -224,8 +234,8 @@ public async Task CanAssociateFiles() } FileFromStoreRemovalResult removalResult = IsAsync - ? await client.RemoveFileFromStoreAsync(vectorStore, files[0]) - : client.RemoveFileFromStore(vectorStore, files[0]); + ? await client.RemoveFileFromStoreAsync(vectorStore.Id, files[0].Id) + : client.RemoveFileFromStore(vectorStore.Id, files[0].Id); Assert.That(removalResult.FileId, Is.EqualTo(files[0].Id)); Assert.True(removalResult.Removed); _associationsToRemove.RemoveAt(0); @@ -237,7 +247,7 @@ public async Task CanAssociateFiles() if (IsAsync) { - await foreach (VectorStoreFileAssociation association in client.GetFileAssociationsAsync(vectorStore)) + await foreach (VectorStoreFileAssociation association in client.GetFileAssociationsAsync(vectorStore.Id)) { count++; Assert.That(association.FileId, Is.Not.EqualTo(files[0].Id)); @@ -246,7 +256,7 @@ public async Task CanAssociateFiles() } else { - foreach (VectorStoreFileAssociation association in client.GetFileAssociations(vectorStore)) + foreach (VectorStoreFileAssociation association in client.GetFileAssociations(vectorStore.Id)) { count++; Assert.That(association.FileId, Is.Not.EqualTo(files[0].Id)); @@ -262,14 +272,16 @@ public async Task Pagination_CanRehydrateFileAssociationCollectionAsync() AssertAsyncOnly(); VectorStoreClient client = GetTestClient(); - VectorStore vectorStore = client.CreateVectorStore(); + CreateVectorStoreOperation createOperation = await client.CreateVectorStoreAsync(waitUntilCompleted: false); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); IReadOnlyList files = GetNewTestFiles(6); foreach (OpenAIFile file in files) { - VectorStoreFileAssociation association = client.AddFileToVectorStore(vectorStore, file); + AddFileToVectorStoreOperation addOperation = await client.AddFileToVectorStoreAsync(vectorStore.Id, file.Id, waitUntilCompleted: false); + VectorStoreFileAssociation association = addOperation.Value; Validate(association); Assert.Multiple(() => { @@ -281,7 +293,7 @@ public async Task Pagination_CanRehydrateFileAssociationCollectionAsync() }); } - FileFromStoreRemovalResult removalResult = client.RemoveFileFromStore(vectorStore, files[0]); + FileFromStoreRemovalResult removalResult = await client.RemoveFileFromStoreAsync(vectorStore.Id, files[0].Id); Assert.That(removalResult.FileId, Is.EqualTo(files[0].Id)); Assert.True(removalResult.Removed); _associationsToRemove.RemoveAt(0); @@ -293,7 +305,7 @@ public async Task Pagination_CanRehydrateFileAssociationCollectionAsync() // Use enumerators instead of enumerables to faciliate advancing the collections // at the same time. - AsyncCollectionResult fileAssociations = client.GetFileAssociationsAsync(vectorStore, new VectorStoreFileAssociationCollectionOptions() { PageSizeLimit = 2 }); + AsyncCollectionResult fileAssociations = client.GetFileAssociationsAsync(vectorStore.Id, new VectorStoreFileAssociationCollectionOptions() { PageSizeLimit = 2 }); IAsyncEnumerable pages = fileAssociations.GetRawPagesAsync(); IAsyncEnumerator pageEnumerator = pages.GetAsyncEnumerator(); await pageEnumerator.MoveNextAsync(); @@ -342,14 +354,16 @@ public void Pagination_CanRehydrateFileAssociationCollection() AssertSyncOnly(); VectorStoreClient client = GetTestClient(); - VectorStore vectorStore = client.CreateVectorStore(); + CreateVectorStoreOperation createOperation = client.CreateVectorStore(waitUntilCompleted: true); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); IReadOnlyList files = GetNewTestFiles(6); foreach (OpenAIFile file in files) { - VectorStoreFileAssociation association = client.AddFileToVectorStore(vectorStore, file); + AddFileToVectorStoreOperation addOperation = client.AddFileToVectorStore(vectorStore.Id, file.Id, waitUntilCompleted: false); + VectorStoreFileAssociation association = addOperation.Value; Validate(association); Assert.Multiple(() => { @@ -361,7 +375,7 @@ public void Pagination_CanRehydrateFileAssociationCollection() }); } - FileFromStoreRemovalResult removalResult = client.RemoveFileFromStore(vectorStore, files[0]); + FileFromStoreRemovalResult removalResult = client.RemoveFileFromStore(vectorStore.Id, files[0].Id); Assert.That(removalResult.FileId, Is.EqualTo(files[0].Id)); Assert.True(removalResult.Removed); _associationsToRemove.RemoveAt(0); @@ -369,7 +383,7 @@ public void Pagination_CanRehydrateFileAssociationCollection() // Errata: removals aren't immediately reflected when requesting the list Thread.Sleep(2000); - CollectionResult fileAssociations = client.GetFileAssociations(vectorStore, new VectorStoreFileAssociationCollectionOptions() { PageSizeLimit = 2 }); + CollectionResult fileAssociations = client.GetFileAssociations(vectorStore.Id, new VectorStoreFileAssociationCollectionOptions() { PageSizeLimit = 2 }); IEnumerable pages = fileAssociations.GetRawPages(); IEnumerator pageEnumerator = pages.GetEnumerator(); pageEnumerator.MoveNext(); @@ -424,29 +438,27 @@ private static IEnumerable GetFileAssociationsFromPa public async Task CanUseBatchIngestion() { VectorStoreClient client = GetTestClient(); - VectorStore vectorStore = client.CreateVectorStore(); + CreateVectorStoreOperation createOperation = client.CreateVectorStore(waitUntilCompleted: true); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); IReadOnlyList testFiles = GetNewTestFiles(5); - VectorStoreBatchFileJob batchJob = client.CreateBatchFileJob(vectorStore, testFiles); - Validate(batchJob); + CreateBatchFileJobOperation batchFileJobOperation = client.CreateBatchFileJob(vectorStore.Id, testFiles?.Select(file => file.Id), waitUntilCompleted: false); + Validate(batchFileJobOperation); Assert.Multiple(() => { - Assert.That(batchJob.BatchId, Is.Not.Null); - Assert.That(batchJob.VectorStoreId, Is.EqualTo(vectorStore.Id)); - Assert.That(batchJob.Status, Is.EqualTo(VectorStoreBatchFileJobStatus.InProgress)); + Assert.That(batchFileJobOperation.BatchId, Is.Not.Null); + Assert.That(batchFileJobOperation.VectorStoreId, Is.EqualTo(vectorStore.Id)); + Assert.That(batchFileJobOperation.Status, Is.EqualTo(VectorStoreBatchFileJobStatus.InProgress)); }); - for (int i = 0; i < 10 && client.GetBatchFileJob(batchJob).Value.Status != VectorStoreBatchFileJobStatus.Completed; i++) - { - Thread.Sleep(500); - } + batchFileJobOperation.WaitForCompletion(); if (IsAsync) { - await foreach (VectorStoreFileAssociation association in client.GetFileAssociationsAsync(batchJob)) + await foreach (VectorStoreFileAssociation association in client.GetFileAssociationsAsync(batchFileJobOperation.VectorStoreId)) { Assert.Multiple(() => { @@ -461,7 +473,7 @@ public async Task CanUseBatchIngestion() } else { - foreach (VectorStoreFileAssociation association in client.GetFileAssociations(batchJob)) + foreach (VectorStoreFileAssociation association in client.GetFileAssociations(batchFileJobOperation.VectorStoreId)) { Assert.Multiple(() => { @@ -476,6 +488,46 @@ public async Task CanUseBatchIngestion() } } + [Test] + public void CanRehydrateBatchFileJob() + { + VectorStoreClient client = GetTestClient(); + CreateVectorStoreOperation createOperation = client.CreateVectorStore(waitUntilCompleted: true); + VectorStore vectorStore = createOperation.Value; + Validate(vectorStore); + + IReadOnlyList testFiles = GetNewTestFiles(5); + + CreateBatchFileJobOperation batchOperation = client.CreateBatchFileJob(vectorStore.Id, testFiles?.Select(file => file.Id), waitUntilCompleted: false); + Validate(batchOperation); + + // Simulate rehydration of the operation + BinaryData rehydrationBytes = batchOperation.RehydrationToken.ToBytes(); + ContinuationToken rehydrationToken = ContinuationToken.FromBytes(rehydrationBytes); + + CreateBatchFileJobOperation rehydratedOperation = CreateBatchFileJobOperation.Rehydrate(client, rehydrationToken); + Validate(rehydratedOperation); + + Assert.Multiple(() => + { + Assert.That(batchOperation.BatchId, Is.Not.Null); + Assert.That(batchOperation.VectorStoreId, Is.EqualTo(vectorStore.Id)); + Assert.That(batchOperation.Status, Is.EqualTo(VectorStoreBatchFileJobStatus.InProgress)); + + Assert.That(rehydratedOperation.BatchId, Is.EqualTo(batchOperation.BatchId)); + Assert.That(rehydratedOperation.VectorStoreId, Is.EqualTo(vectorStore.Id)); + Assert.That(rehydratedOperation.Status, Is.EqualTo(VectorStoreBatchFileJobStatus.InProgress)); + }); + + Task.WaitAll( + Task.Run(() => batchOperation.WaitForCompletion()), + Task.Run(() => rehydratedOperation.WaitForCompletion())); + + Assert.IsTrue(batchOperation.HasCompleted); + Assert.IsTrue(rehydratedOperation.HasCompleted); + Assert.AreEqual(batchOperation.Status, rehydratedOperation.Status); + } + public enum ChunkingStrategyKind { Auto, Static } [Test] @@ -508,16 +560,16 @@ public async Task CanApplyChunkingStrategy(ChunkingStrategyKind strategyKind) { creationOptions.FileIds.Add(file.Id); } - VectorStore vectorStore = IsAsync - ? await client.CreateVectorStoreAsync(creationOptions) - : client.CreateVectorStore(creationOptions); - + CreateVectorStoreOperation createOperation = IsAsync + ? await client.CreateVectorStoreAsync(waitUntilCompleted: true, creationOptions) + : client.CreateVectorStore(waitUntilCompleted: true, creationOptions); + VectorStore vectorStore = createOperation.Value; Validate(vectorStore); Assert.That(vectorStore.FileCounts.Total, Is.EqualTo(5)); if (IsAsync) { - AsyncCollectionResult associations = client.GetFileAssociationsAsync(vectorStore); + AsyncCollectionResult associations = client.GetFileAssociationsAsync(vectorStore.Id); await foreach (VectorStoreFileAssociation association in associations) { @@ -541,7 +593,7 @@ public async Task CanApplyChunkingStrategy(ChunkingStrategyKind strategyKind) } else { - CollectionResult associations = client.GetFileAssociations(vectorStore); + CollectionResult associations = client.GetFileAssociations(vectorStore.Id); foreach (VectorStoreFileAssociation association in associations) { @@ -592,9 +644,10 @@ protected void Cleanup() { ErrorOptions = ClientErrorBehaviors.NoThrow, }; - foreach (VectorStoreBatchFileJob job in _jobsToCancel) + + foreach (CreateBatchFileJobOperation job in _jobsToCancel) { - ClientResult protocolResult = vectorStoreClient.CancelBatchFileJob(job.VectorStoreId, job.BatchId, requestOptions); + ClientResult protocolResult = job.Cancel(requestOptions); Console.WriteLine($"Cleanup: {job.BatchId} => {protocolResult?.GetRawResponse()?.Status}"); } foreach (VectorStoreFileAssociation association in _associationsToRemove) @@ -623,7 +676,7 @@ protected void Cleanup() /// The provided instance type isn't supported. private void Validate(T target) { - if (target is VectorStoreBatchFileJob job) + if (target is CreateBatchFileJobOperation job) { Assert.That(job.BatchId, Is.Not.Null); _jobsToCancel.Add(job); diff --git a/tests/Assistants/AssistantsTests.cs b/tests/Assistants/AssistantsTests.cs index c22a6e97..5fe59b02 100644 --- a/tests/Assistants/AssistantsTests.cs +++ b/tests/Assistants/AssistantsTests.cs @@ -194,8 +194,8 @@ public async Task BasicThreadOperationsWork() }, }; thread = IsAsync - ? await client.ModifyThreadAsync(thread, modificationOptions) - : client.ModifyThread(thread, modificationOptions); + ? await client.ModifyThreadAsync(thread.Id, modificationOptions) + : client.ModifyThread(thread.Id, modificationOptions); Assert.That(thread.Metadata.TryGetValue("threadMetadata", out threadMetadataValue) && threadMetadataValue == "newThreadMetadataValue"); } @@ -206,16 +206,16 @@ public async Task BasicMessageOperationsWork() AssistantThread thread = client.CreateThread(); Validate(thread); ThreadMessage message = IsAsync - ? await client.CreateMessageAsync(thread, MessageRole.User, ["Hello, world!"]) - : client.CreateMessage(thread, MessageRole.User, ["Hello, world!"]); + ? await client.CreateMessageAsync(thread.Id, MessageRole.User, ["Hello, world!"]) + : client.CreateMessage(thread.Id, MessageRole.User, ["Hello, world!"]); Validate(message); Assert.That(message.CreatedAt, Is.GreaterThan(s_2024)); Assert.That(message.Content?.Count, Is.EqualTo(1)); Assert.That(message.Content[0], Is.Not.Null); Assert.That(message.Content[0].Text, Is.EqualTo("Hello, world!")); MessageDeletionResult deletionResult = IsAsync - ? await client.DeleteMessageAsync(message) - : client.DeleteMessage(message); + ? await client.DeleteMessageAsync(message.ThreadId, message.Id) + : client.DeleteMessage(message.ThreadId, message.Id); Assert.That(deletionResult.MessageId, Is.EqualTo(message.Id)); Assert.That(deletionResult.Deleted, Is.True); _messagesToDelete.Remove(message); @@ -228,14 +228,14 @@ public async Task BasicMessageOperationsWork() }, }; message = IsAsync - ? await client.CreateMessageAsync(thread, MessageRole.User, ["Goodbye, world!"], creationOptions) - : client.CreateMessage(thread, MessageRole.User, ["Goodbye, world!"], creationOptions); + ? await client.CreateMessageAsync(thread.Id, MessageRole.User, ["Goodbye, world!"], creationOptions) + : client.CreateMessage(thread.Id, MessageRole.User, ["Goodbye, world!"], creationOptions); Validate(message); Assert.That(message.Metadata.TryGetValue("messageMetadata", out string metadataValue) && metadataValue == "messageMetadataValue"); ThreadMessage retrievedMessage = IsAsync - ? await client.GetMessageAsync(thread.Id, message.Id) - : client.GetMessage(thread.Id, message.Id); + ? await client.GetMessageAsync(message.ThreadId, message.Id) + : client.GetMessage(message.ThreadId, message.Id); Assert.That(retrievedMessage.Id, Is.EqualTo(message.Id)); MessageModificationOptions modificationOptions = new MessageModificationOptions() @@ -246,13 +246,13 @@ public async Task BasicMessageOperationsWork() } }; message = IsAsync - ? await client.ModifyMessageAsync(message, modificationOptions) - : client.ModifyMessage(message, modificationOptions); + ? await client.ModifyMessageAsync(message.ThreadId, message.Id, modificationOptions) + : client.ModifyMessage(message.ThreadId, message.Id, modificationOptions); Assert.That(message.Metadata.TryGetValue("messageMetadata", out metadataValue) && metadataValue == "newValue"); List messages = IsAsync - ? await client.GetMessagesAsync(thread).ToListAsync() - : [.. client.GetMessages(thread)]; + ? await client.GetMessagesAsync(thread.Id).ToListAsync() + : [.. client.GetMessages(thread.Id)]; Assert.That(messages.Count, Is.EqualTo(1)); Assert.That(messages[0].Id, Is.EqualTo(message.Id)); @@ -288,8 +288,8 @@ public async Task ThreadWithInitialMessagesWorks() Validate(thread); MessageCollectionOptions collectionOptions = new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending }; List messages = IsAsync - ? await client.GetMessagesAsync(thread, collectionOptions).ToListAsync() - : client.GetMessages(thread, collectionOptions).ToList(); + ? await client.GetMessagesAsync(thread.Id, collectionOptions).ToListAsync() + : client.GetMessages(thread.Id, collectionOptions).ToList(); Assert.That(messages.Count, Is.EqualTo(2)); Assert.That(messages[0].Role, Is.EqualTo(MessageRole.User)); Assert.That(messages[0].Content?.Count, Is.EqualTo(1)); @@ -310,8 +310,8 @@ public async Task BasicRunOperationsWork() AssistantThread thread = client.CreateThread(); Validate(thread); List runs = IsAsync - ? await client.GetRunsAsync(thread).ToListAsync() - : client.GetRuns(thread).ToList(); + ? await client.GetRunsAsync(thread.Id).ToListAsync() + : client.GetRuns(thread.Id).ToList(); Assert.That(runs.Count, Is.EqualTo(0)); ThreadMessage message = client.CreateMessage(thread.Id, MessageRole.User, ["Hello, assistant!"]); Validate(message); @@ -322,25 +322,25 @@ public async Task BasicRunOperationsWork() Assert.That(run.Status, Is.EqualTo(RunStatus.Queued)); Assert.That(run.CreatedAt, Is.GreaterThan(s_2024)); ThreadRun retrievedRun = IsAsync - ? await client.GetRunAsync(thread.Id, run.Id) - : client.GetRun(thread.Id, run.Id); + ? await client.GetRunAsync(run.ThreadId, run.Id) + : client.GetRun(run.ThreadId, run.Id); Assert.That(retrievedRun.Id, Is.EqualTo(run.Id)); runs = IsAsync - ? await client.GetRunsAsync(thread).ToListAsync() - : client.GetRuns(thread).ToList(); + ? await client.GetRunsAsync(thread.Id).ToListAsync() + : client.GetRuns(thread.Id).ToList(); Assert.That(runs.Count, Is.EqualTo(1)); Assert.That(runs[0].Id, Is.EqualTo(run.Id)); List messages = IsAsync ? - await client.GetMessagesAsync(thread).ToListAsync() : - client.GetMessages(thread).ToList(); + await client.GetMessagesAsync(thread.Id).ToListAsync() : + client.GetMessages(thread.Id).ToList(); Assert.That(messages.Count, Is.GreaterThanOrEqualTo(1)); for (int i = 0; i < 10 && !run.Status.IsTerminal; i++) { Thread.Sleep(1000); run = IsAsync - ? await client.GetRunAsync(run) - : client.GetRun(run); + ? await client.GetRunAsync(run.ThreadId, run.Id) + : client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); Assert.That(run.CompletedAt, Is.GreaterThan(s_2024)); @@ -350,8 +350,8 @@ await client.GetMessagesAsync(thread).ToListAsync() : Assert.That(run.IncompleteDetails, Is.Null); messages = IsAsync ? - await client.GetMessagesAsync(thread).ToListAsync() : - client.GetMessages(thread).ToList(); + await client.GetMessagesAsync(thread.Id).ToListAsync() : + client.GetMessages(thread.Id).ToList(); Assert.That(messages.Count, Is.EqualTo(2)); Assert.That(messages[0].Role, Is.EqualTo(MessageRole.Assistant)); @@ -400,20 +400,20 @@ public async Task BasicRunStepFunctionalityWorks() }); Validate(thread); - ThreadRun run = client.CreateRun(thread, assistant); + ThreadRun run = client.CreateRun(thread.Id, assistant.Id); Validate(run); while (!run.Status.IsTerminal) { Thread.Sleep(1000); - run = client.GetRun(run); + run = client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); Assert.That(run.Usage?.TotalTokenCount, Is.GreaterThan(0)); List runSteps = IsAsync - ? await client.GetRunStepsAsync(run).ToListAsync() - : client.GetRunSteps(run).ToList(); + ? await client.GetRunStepsAsync(run.ThreadId, run.Id).ToListAsync() + : client.GetRunSteps(run.ThreadId, run.Id).ToList(); RunStep firstStep = runSteps[0]; RunStep secondStep = runSteps[1]; @@ -459,20 +459,20 @@ public async Task SettingResponseFormatWorks() ResponseFormat = AssistantResponseFormat.CreateTextFormat(), }; assistant = IsAsync - ? await client.ModifyAssistantAsync(assistant, modificationOptions) - : client.ModifyAssistant(assistant, modificationOptions); + ? await client.ModifyAssistantAsync(assistant.Id, modificationOptions) + : client.ModifyAssistant(assistant.Id, modificationOptions); Assert.That(assistant.ResponseFormat == AssistantResponseFormat.CreateTextFormat()); AssistantThread thread = client.CreateThread(); Validate(thread); - ThreadMessage message = client.CreateMessage(thread, MessageRole.User, ["Write some JSON for me!"]); + ThreadMessage message = client.CreateMessage(thread.Id, MessageRole.User, ["Write some JSON for me!"]); Validate(message); RunCreationOptions runCreationOptions = new RunCreationOptions() { ResponseFormat = AssistantResponseFormat.CreateJsonObjectFormat(), }; ThreadRun run = IsAsync - ? await client.CreateRunAsync(thread, assistant, runCreationOptions) - : client.CreateRun(thread, assistant, runCreationOptions); + ? await client.CreateRunAsync(thread.Id, assistant.Id, runCreationOptions) + : client.CreateRun(thread.Id, assistant.Id, runCreationOptions); Assert.That(run.ResponseFormat == AssistantResponseFormat.CreateJsonObjectFormat()); } @@ -522,16 +522,16 @@ public async Task FunctionToolsWork() AdditionalInstructions = "Call provided tools when appropriate.", }; ThreadRun run = IsAsync - ? await client.CreateThreadAndRunAsync(assistant, threadCreationOptions, runCreationOptions) - : client.CreateThreadAndRun(assistant, threadCreationOptions, runCreationOptions); + ? await client.CreateThreadAndRunAsync(assistant.Id, threadCreationOptions, runCreationOptions) + : client.CreateThreadAndRun(assistant.Id, threadCreationOptions, runCreationOptions); Validate(run); for (int i = 0; i < 10 && !run.Status.IsTerminal; i++) { Thread.Sleep(1000); run = IsAsync - ? await client.GetRunAsync(run) - : client.GetRun(run); + ? await client.GetRunAsync(run.ThreadId, run.Id) + : client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.RequiresAction)); Assert.That(run.RequiredActions?.Count, Is.EqualTo(1)); @@ -540,16 +540,16 @@ public async Task FunctionToolsWork() Assert.That(run.RequiredActions[0].FunctionArguments, Is.Not.Null.And.Not.Empty); run = IsAsync - ? await client.SubmitToolOutputsToRunAsync(run, [new(run.RequiredActions[0].ToolCallId, "tacos")]) - : client.SubmitToolOutputsToRun(run, [new(run.RequiredActions[0].ToolCallId, "tacos")]); + ? await client.SubmitToolOutputsToRunAsync(run.ThreadId, run.Id, [new(run.RequiredActions[0].ToolCallId, "tacos")]) + : client.SubmitToolOutputsToRun(run.ThreadId, run.Id, [new(run.RequiredActions[0].ToolCallId, "tacos")]); Assert.That(run.Status.IsTerminal, Is.False); for (int i = 0; i < 10 && !run.Status.IsTerminal; i++) { Thread.Sleep(1000); run = IsAsync - ? await client.GetRunAsync(run) - : client.GetRun(run); + ? await client.GetRunAsync(run.ThreadId, run.Id) + : client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); @@ -682,7 +682,7 @@ public async Task StreamingToolCallAsync() Print(" >>> Beginning call ... "); AsyncCollectionResult asyncResults = client.CreateThreadAndRunStreamingAsync( - assistant, + assistant.Id, new() { InitialMessages = { "What should I wear outside right now?", }, @@ -719,7 +719,7 @@ public async Task StreamingToolCallAsync() } if (toolOutputs.Count > 0) { - asyncResults = client.SubmitToolOutputsToRunStreamingAsync(run, toolOutputs); + asyncResults = client.SubmitToolOutputsToRunStreamingAsync(run.ThreadId, run.Id, toolOutputs); } } while (run?.Status.IsTerminal == false); } @@ -746,7 +746,7 @@ public void StreamingToolCall() Print(" >>> Beginning call ... "); CollectionResult results = client.CreateThreadAndRunStreaming( - assistant, + assistant.Id, new() { InitialMessages = { "What should I wear outside right now?", }, @@ -783,7 +783,7 @@ public void StreamingToolCall() } if (toolOutputs.Count > 0) { - results = client.SubmitToolOutputsToRunStreaming(run, toolOutputs); + results = client.SubmitToolOutputsToRunStreaming(run.ThreadId, run.Id, toolOutputs); } } while (run?.Status.IsTerminal == false); } @@ -855,8 +855,8 @@ This file describes the favorite foods of several people. }, }; assistant = IsAsync - ? await client.ModifyAssistantAsync(assistant, modificationOptions) - : client.ModifyAssistant(assistant, modificationOptions); + ? await client.ModifyAssistantAsync(assistant.Id, modificationOptions) + : client.ModifyAssistant(assistant.Id, modificationOptions); Assert.That(assistant.ToolResources?.FileSearch?.VectorStoreIds, Has.Count.EqualTo(1)); Assert.That(assistant.ToolResources.FileSearch.VectorStoreIds[0], Is.EqualTo(createdVectorStoreId)); @@ -892,25 +892,25 @@ This file describes the favorite foods of several people. } }; thread = IsAsync - ? await client.ModifyThreadAsync(thread, threadModificationOptions) - : client.ModifyThread(thread, threadModificationOptions); + ? await client.ModifyThreadAsync(thread.Id, threadModificationOptions) + : client.ModifyThread(thread.Id, threadModificationOptions); Assert.That(thread.ToolResources?.FileSearch?.VectorStoreIds, Has.Count.EqualTo(1)); Assert.That(thread.ToolResources.FileSearch.VectorStoreIds[0], Is.EqualTo(createdVectorStoreId)); ThreadRun run = IsAsync - ? await client.CreateRunAsync(thread, assistant) - : client.CreateRun(thread, assistant); + ? await client.CreateRunAsync(thread.Id, assistant.Id) + : client.CreateRun(thread.Id, assistant.Id); Validate(run); do { Thread.Sleep(1000); run = IsAsync - ? await client.GetRunAsync(run) - : client.GetRun(run); + ? await client.GetRunAsync(run.ThreadId, run.Id) + : client.GetRun(run.ThreadId, run.Id); } while (run?.Status.IsTerminal == false); Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); - CollectionResult messages = client.GetMessages(thread, new() { Order = MessageCollectionOrder.Descending }); + CollectionResult messages = client.GetMessages(thread.Id, new() { Order = MessageCollectionOrder.Descending }); int messageCount = 0; bool hasCake = false; foreach (ThreadMessage message in messages) @@ -1686,13 +1686,13 @@ public async Task Pagination_CanRehydrateRunStepPageCollectionFromBytesAsync() }); Validate(thread); - ThreadRun run = client.CreateRun(thread, assistant); + ThreadRun run = client.CreateRun(thread.Id, assistant.Id); Validate(run); while (!run.Status.IsTerminal) { Thread.Sleep(1000); - run = client.GetRun(run); + run = client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); Assert.That(run.Usage?.TotalTokenCount, Is.GreaterThan(0)); @@ -1701,7 +1701,7 @@ public async Task Pagination_CanRehydrateRunStepPageCollectionFromBytesAsync() IReadOnlyList rehydratedRunSteps; { const int numPerPage = 2; - AsyncCollectionResult results = client.GetRunStepsAsync(run, new() { PageSizeLimit = numPerPage }); + AsyncCollectionResult results = client.GetRunStepsAsync(run.ThreadId, run.Id, new() { PageSizeLimit = numPerPage }); runSteps = await results .Skip(numPerPage) .Select(r => r.Id) @@ -1761,13 +1761,13 @@ public void Pagination_CanRehydrateRunStepPageCollectionFromBytes() }); Validate(thread); - ThreadRun run = client.CreateRun(thread, assistant); + ThreadRun run = client.CreateRun(thread.Id, assistant.Id); Validate(run); while (!run.Status.IsTerminal) { Thread.Sleep(1000); - run = client.GetRun(run); + run = client.GetRun(run.ThreadId, run.Id); } Assert.That(run.Status, Is.EqualTo(RunStatus.Completed)); Assert.That(run.Usage?.TotalTokenCount, Is.GreaterThan(0)); @@ -1776,7 +1776,7 @@ public void Pagination_CanRehydrateRunStepPageCollectionFromBytes() IReadOnlyList rehydratedRunSteps; { const int numPerPage = 2; - CollectionResult results = client.GetRunSteps(run, new() { PageSizeLimit = numPerPage }); + CollectionResult results = client.GetRunSteps(run.ThreadId, run.Id, new() { PageSizeLimit = numPerPage }); runSteps = results .Skip(numPerPage) .Select(r => r.Id) @@ -1816,14 +1816,14 @@ async ValueTask RefreshMessageListAsync() messages.Clear(); if (IsAsync) { - await foreach (ThreadMessage message in client.GetMessagesAsync(thread)) + await foreach (ThreadMessage message in client.GetMessagesAsync(thread.Id)) { messages.Add(message); } } else { - foreach (ThreadMessage message in client.GetMessages(thread)) + foreach (ThreadMessage message in client.GetMessages(thread.Id)) { messages.Add(message); } @@ -1840,11 +1840,11 @@ async ValueTask RefreshMessageListAsync() Assert.That(messages[0].Content[0].Text, Is.EqualTo(assistantMessageText)); Assert.That(messages[0].Content[0].Text, Is.EqualTo(assistantMessageText)); ThreadMessage userMessage = IsAsync - ? await client.CreateMessageAsync(thread, MessageRole.User, [MessageContent.FromText(userMessageText)]) - : client.CreateMessage(thread, MessageRole.User, [MessageContent.FromText(userMessageText)]); + ? await client.CreateMessageAsync(thread.Id, MessageRole.User, [MessageContent.FromText(userMessageText)]) + : client.CreateMessage(thread.Id, MessageRole.User, [MessageContent.FromText(userMessageText)]); ThreadMessage assistantMessage = IsAsync - ? await client.CreateMessageAsync(thread, MessageRole.Assistant, [assistantMessageText]) - : client.CreateMessage(thread, MessageRole.Assistant, [assistantMessageText]); + ? await client.CreateMessageAsync(thread.Id, MessageRole.Assistant, [assistantMessageText]) + : client.CreateMessage(thread.Id, MessageRole.Assistant, [assistantMessageText]); await RefreshMessageListAsync(); Assert.That(messages.Count, Is.EqualTo(4)); Assert.That(messages[3].Role, Is.EqualTo(MessageRole.User)); diff --git a/tests/Audio/OpenAIAudioModelFactoryTests.cs b/tests/Audio/OpenAIAudioModelFactoryTests.cs index 60707dae..4e368e5a 100644 --- a/tests/Audio/OpenAIAudioModelFactoryTests.cs +++ b/tests/Audio/OpenAIAudioModelFactoryTests.cs @@ -166,7 +166,7 @@ public void TranscribedSegmentWithNoPropertiesWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -184,7 +184,7 @@ public void TranscribedSegmentWithIdWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -202,7 +202,7 @@ public void TranscribedSegmentWithSeekOffsetWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -220,7 +220,7 @@ public void TranscribedSegmentWithStartWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(startTime)); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -238,7 +238,7 @@ public void TranscribedSegmentWithEndWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(endTime)); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -256,7 +256,7 @@ public void TranscribedSegmentWithTextWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.EqualTo(text)); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -266,7 +266,7 @@ public void TranscribedSegmentWithTextWorks() [Test] public void TranscribedSegmentWithTokenIdsWorks() { - IEnumerable tokenIds = [900000000, 900000001]; + ReadOnlyMemory tokenIds = new[] { 900000000, 900000001 }; TranscribedSegment transcribedSegment = OpenAIAudioModelFactory.TranscribedSegment(tokenIds: tokenIds); Assert.That(transcribedSegment.Id, Is.EqualTo(default(int))); @@ -274,7 +274,7 @@ public void TranscribedSegmentWithTokenIdsWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds.SequenceEqual(tokenIds), Is.True); + Assert.That(transcribedSegment.TokenIds.Span.SequenceEqual(tokenIds.Span), Is.True); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -292,7 +292,7 @@ public void TranscribedSegmentWithTemperatureWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(temperature)); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -310,7 +310,7 @@ public void TranscribedSegmentWithAverageLogProbabilityWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(averageLogProbability)); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); @@ -328,7 +328,7 @@ public void TranscribedSegmentWithCompressionRatioWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(compressionRatio)); @@ -346,7 +346,7 @@ public void TranscribedSegmentWithNoSpeechProbabilityWorks() Assert.That(transcribedSegment.StartTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.EndTime, Is.EqualTo(default(TimeSpan))); Assert.That(transcribedSegment.Text, Is.Null); - Assert.That(transcribedSegment.TokenIds, Is.Not.Null.And.Empty); + Assert.That(transcribedSegment.TokenIds.Span.Length, Is.GreaterThanOrEqualTo(0)); Assert.That(transcribedSegment.Temperature, Is.EqualTo(default(float))); Assert.That(transcribedSegment.AverageLogProbability, Is.EqualTo(default(float))); Assert.That(transcribedSegment.CompressionRatio, Is.EqualTo(default(float))); diff --git a/tests/Audio/TranscriptionMockTests.cs b/tests/Audio/TranscriptionMockTests.cs index 6543c10d..7008ac4e 100644 --- a/tests/Audio/TranscriptionMockTests.cs +++ b/tests/Audio/TranscriptionMockTests.cs @@ -131,7 +131,7 @@ public async Task TranscribeAudioDeserializesSegment(AudioSourceKind audioSource Assert.That(segment.StartTime, Is.EqualTo(TimeSpan.FromSeconds(2.5))); Assert.That(segment.EndTime, Is.EqualTo(TimeSpan.FromSeconds(7.5))); Assert.That(segment.Text, Is.EqualTo("The quick brown fox got lost.")); - Assert.That(segment.TokenIds.SequenceEqual([255, 305, 678])); + Assert.That(segment.TokenIds.Span.SequenceEqual([255, 305, 678]), Is.True); Assert.That(segment.Temperature, Is.EqualTo(0.8f)); Assert.That(segment.AverageLogProbability, Is.EqualTo(-0.3f)); Assert.That(segment.CompressionRatio, Is.EqualTo(1.5f)); diff --git a/tests/Audio/TranscriptionTests.cs b/tests/Audio/TranscriptionTests.cs index 881ddac9..3759d714 100644 --- a/tests/Audio/TranscriptionTests.cs +++ b/tests/Audio/TranscriptionTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using System.Transactions; using static OpenAI.Tests.TestHelpers; namespace OpenAI.Tests.Audio; @@ -71,7 +72,7 @@ public async Task TimestampsWork(AudioTimestampGranularities granularityFlags) { ResponseFormat = AudioTranscriptionFormat.Verbose, Temperature = 0.4f, - Granularities = granularityFlags, + TimestampGranularities = granularityFlags, }; ClientResult transcriptionResult = IsAsync @@ -117,8 +118,8 @@ public async Task TimestampsWork(AudioTimestampGranularities granularityFlags) } Assert.That(segments[i].EndTime, Is.GreaterThan(segments[i].StartTime)); Assert.That(string.IsNullOrEmpty(segments[i].Text), Is.False); - Assert.That(segments[i].TokenIds, Is.Not.Null.And.Not.Empty); - foreach (int tokenId in segments[i].TokenIds) + Assert.That(segments[i].TokenIds.Span.Length, Is.GreaterThan(0)); + foreach (int tokenId in segments[i].TokenIds.ToArray()) { Assert.That(tokenId, Is.GreaterThanOrEqualTo(0)); } @@ -130,26 +131,68 @@ public async Task TimestampsWork(AudioTimestampGranularities granularityFlags) } [Test] - [TestCase(AudioTranscriptionFormat.Simple)] - [TestCase(AudioTranscriptionFormat.Verbose)] - [TestCase(AudioTranscriptionFormat.Srt)] - [TestCase(AudioTranscriptionFormat.Vtt)] - public async Task TranscriptionFormatsWork(AudioTranscriptionFormat formatToTest) + [TestCase("text")] + [TestCase("json")] + [TestCase("verbose_json")] + [TestCase("srt")] + [TestCase("vtt")] + [TestCase(null)] + public async Task TranscriptionFormatsWork(string responseFormat) { AudioClient client = GetTestClient(TestScenario.Audio_Whisper); string path = Path.Combine("Assets", "audio_hello_world.mp3"); AudioTranscriptionOptions options = new() { - ResponseFormat = formatToTest, + ResponseFormat = responseFormat switch + { + "text" => AudioTranscriptionFormat.Text, + "json" => AudioTranscriptionFormat.Simple, + "verbose_json" => AudioTranscriptionFormat.Verbose, + "srt" => AudioTranscriptionFormat.Srt, + "vtt" => AudioTranscriptionFormat.Vtt, + _ => (AudioTranscriptionFormat?)null + } }; AudioTranscription transcription = IsAsync ? await client.TranscribeAudioAsync(path, options) : client.TranscribeAudio(path, options); - Assert.That(transcription, Is.Not.Null); - Assert.That(transcription.Text, Is.Not.Null.And.Not.Empty); - Assert.That(transcription.Text.ToLowerInvariant(), Does.Contain("hello")); + + Assert.That(transcription?.Text?.ToLowerInvariant(), Does.Contain("hello")); + + + if (options.ResponseFormat == AudioTranscriptionFormat.Verbose) + { + Assert.That(transcription.Language, Is.EqualTo("english")); + Assert.That(transcription.Duration, Is.GreaterThan(TimeSpan.Zero)); + Assert.That(transcription.Segments, Is.Not.Empty); + + for (int i = 0; i < transcription.Segments.Count; i++) + { + TranscribedSegment segment = transcription.Segments[i]; + + if (i > 0) + { + Assert.That(segment.StartTime, Is.GreaterThanOrEqualTo(transcription.Segments[i - 1].EndTime)); + } + + Assert.That(segment.Id, Is.EqualTo(i)); + Assert.That(segment.EndTime, Is.GreaterThanOrEqualTo(segment.StartTime)); + Assert.That(segment.TokenIds, Is.Not.Null.And.Not.Empty); + + Assert.That(segment.AverageLogProbability, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); + Assert.That(segment.CompressionRatio, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); + Assert.That(segment.NoSpeechProbability, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); + } + } + else + { + Assert.That(transcription.Duration, Is.Null); + Assert.That(transcription.Language, Is.Null); + Assert.That(transcription.Segments, Is.Not.Null.And.Empty); + Assert.That(transcription.Words, Is.Not.Null.And.Empty); + } } [Test] diff --git a/tests/Audio/TranslationMockTests.cs b/tests/Audio/TranslationMockTests.cs index e0d43014..e9dfcd71 100644 --- a/tests/Audio/TranslationMockTests.cs +++ b/tests/Audio/TranslationMockTests.cs @@ -107,7 +107,7 @@ public async Task TranslateAudioDeserializesSegment(AudioSourceKind audioSourceK Assert.That(segment.StartTime, Is.EqualTo(TimeSpan.FromSeconds(2.5))); Assert.That(segment.EndTime, Is.EqualTo(TimeSpan.FromSeconds(7.5))); Assert.That(segment.Text, Is.EqualTo("The quick brown fox got lost.")); - Assert.That(segment.TokenIds.SequenceEqual([255, 305, 678])); + Assert.That(segment.TokenIds.Span.SequenceEqual([255, 305, 678])); Assert.That(segment.Temperature, Is.EqualTo(0.8f)); Assert.That(segment.AverageLogProbability, Is.EqualTo(-0.3f)); Assert.That(segment.CompressionRatio, Is.EqualTo(1.5f)); diff --git a/tests/Audio/TranslationTests.cs b/tests/Audio/TranslationTests.cs index 7c6efb87..a0fe9f79 100644 --- a/tests/Audio/TranslationTests.cs +++ b/tests/Audio/TranslationTests.cs @@ -2,6 +2,7 @@ using OpenAI.Audio; using OpenAI.Tests.Utility; using System; +using System.ClientModel; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -55,27 +56,37 @@ public async Task TranslationWorks(AudioSourceKind audioSourceKind) } [Test] - [TestCase(AudioTranslationFormat.Simple)] - [TestCase(AudioTranslationFormat.Verbose)] - [TestCase(AudioTranslationFormat.Srt)] - [TestCase(AudioTranslationFormat.Vtt)] - public async Task TranslationFormatsWork(AudioTranslationFormat formatToTest) + [TestCase("text")] + [TestCase("json")] + [TestCase("verbose_json")] + [TestCase("srt")] + [TestCase("vtt")] + [TestCase(null)] + public async Task TranslationFormatsWork(string responseFormat) { AudioClient client = GetTestClient(TestScenario.Audio_Whisper); string path = Path.Combine("Assets", "audio_french.wav"); AudioTranslationOptions options = new() { - ResponseFormat = formatToTest + ResponseFormat = responseFormat switch + { + "text" => AudioTranslationFormat.Text, + "json" => AudioTranslationFormat.Simple, + "verbose_json" => AudioTranslationFormat.Verbose, + "srt" => AudioTranslationFormat.Srt, + "vtt" => AudioTranslationFormat.Vtt, + _ => (AudioTranslationFormat?)null + } }; AudioTranslation translation = IsAsync ? await client.TranslateAudioAsync(path, options) : client.TranslateAudio(path, options); - + Assert.That(translation?.Text?.ToLowerInvariant(), Does.Contain("recognition")); - if (formatToTest == AudioTranslationFormat.Verbose) + if (options.ResponseFormat == AudioTranslationFormat.Verbose) { Assert.That(translation.Language, Is.EqualTo("english")); Assert.That(translation.Duration, Is.GreaterThan(TimeSpan.Zero)); @@ -92,12 +103,18 @@ public async Task TranslationFormatsWork(AudioTranslationFormat formatToTest) Assert.That(segment.Id, Is.EqualTo(i)); Assert.That(segment.EndTime, Is.GreaterThanOrEqualTo(segment.StartTime)); - Assert.That(segment.TokenIds, Is.Not.Null.And.Not.Empty); + Assert.That(segment.TokenIds.Span.Length, Is.GreaterThan(0)); Assert.That(segment.AverageLogProbability, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); Assert.That(segment.CompressionRatio, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); Assert.That(segment.NoSpeechProbability, Is.LessThan(-0.001f).Or.GreaterThan(0.001f)); } } + else + { + Assert.That(translation.Duration, Is.Null); + Assert.That(translation.Language, Is.Null); + Assert.That(translation.Segments, Is.Not.Null.And.Empty); + } } } diff --git a/tests/Batch/BatchTests.cs b/tests/Batch/BatchTests.cs index 1eea2986..44c8898b 100644 --- a/tests/Batch/BatchTests.cs +++ b/tests/Batch/BatchTests.cs @@ -51,10 +51,6 @@ public void ListBatchesProtocol() Assert.That(createdAt, Is.GreaterThan(unixTime2024)); } pageCount++; - - //var dynamicResult = result.GetRawResponse().Content.ToDynamicFromJson(); - //Assert.That(dynamicResult.data.Count, Is.GreaterThan(0)); - //Assert.That(dynamicResult.data[0].createdAt, Is.GreaterThan(new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero))); } Assert.GreaterOrEqual(pageCount, 1); @@ -87,10 +83,6 @@ public async Task ListBatchesProtocolAsync() Assert.That(createdAt, Is.GreaterThan(unixTime2024)); } pageCount++; - - //var dynamicResult = result.GetRawResponse().Content.ToDynamicFromJson(); - //Assert.That(dynamicResult.data.Count, Is.GreaterThan(0)); - //Assert.That(dynamicResult.data[0].createdAt, Is.GreaterThan(new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero))); } Assert.GreaterOrEqual(pageCount, 1); @@ -121,11 +113,11 @@ public async Task CreateGetAndCancelBatchProtocol() testMetadataKey = "test metadata value", }, })); - ClientResult batchResult = IsAsync - ? await client.CreateBatchAsync(content) - : client.CreateBatch(content); + CreateBatchOperation batchOperation = IsAsync + ? await client.CreateBatchAsync(content, waitUntilCompleted: false) + : client.CreateBatch(content, waitUntilCompleted: false); - BinaryData response = batchResult.GetRawResponse().Content; + BinaryData response = batchOperation.GetRawResponse().Content; JsonDocument jsonDocument = JsonDocument.Parse(response); JsonElement idElement = jsonDocument.RootElement.GetProperty("id"); @@ -146,34 +138,98 @@ public async Task CreateGetAndCancelBatchProtocol() Assert.That(status, Is.EqualTo("validating")); Assert.That(testMetadataKey, Is.EqualTo("test metadata value")); - batchResult = IsAsync - ? await client.GetBatchAsync(id, options: null) - : client.GetBatch(id, options: null); - JsonElement endpointElement = jsonDocument.RootElement.GetProperty("endpoint"); string endpoint = endpointElement.GetString(); Assert.That(endpoint, Is.EqualTo("/v1/chat/completions")); - batchResult = IsAsync - ? await client.CancelBatchAsync(id, options: null) - : client.CancelBatch(id, options: null); + ClientResult clientResult = IsAsync + ? await batchOperation.CancelAsync(options: null) + : batchOperation.Cancel(options: null); statusElement = jsonDocument.RootElement.GetProperty("status"); status = statusElement.GetString(); Assert.That(status, Is.EqualTo("validating")); + } + + [Test] + public async Task CanRehydrateBatchOperation() + { + using MemoryStream testFileStream = new(); + using StreamWriter streamWriter = new(testFileStream); + string input = @"{""custom_id"": ""request-1"", ""method"": ""POST"", ""url"": ""/v1/chat/completions"", ""body"": {""model"": ""gpt-4o-mini"", ""messages"": [{""role"": ""system"", ""content"": ""You are a helpful assistant.""}, {""role"": ""user"", ""content"": ""What is 2+2?""}]}}"; + streamWriter.WriteLine(input); + streamWriter.Flush(); + testFileStream.Position = 0; + + FileClient fileClient = GetTestClient(TestScenario.Files); + OpenAIFile inputFile = await fileClient.UploadFileAsync(testFileStream, "test-batch-file", FileUploadPurpose.Batch); + Assert.That(inputFile.Id, Is.Not.Null.And.Not.Empty); + + BatchClient client = GetTestClient(); + BinaryContent content = BinaryContent.Create(BinaryData.FromObjectAsJson(new + { + input_file_id = inputFile.Id, + endpoint = "/v1/chat/completions", + completion_window = "24h", + metadata = new + { + testMetadataKey = "test metadata value", + }, + })); + + CreateBatchOperation batchOperation = IsAsync + ? await client.CreateBatchAsync(content, waitUntilCompleted: false) + : client.CreateBatch(content, waitUntilCompleted: false); + + // Simulate rehydration of the operation + BinaryData rehydrationBytes = batchOperation.RehydrationToken.ToBytes(); + ContinuationToken rehydrationToken = ContinuationToken.FromBytes(rehydrationBytes); + + CreateBatchOperation rehydratedOperation = IsAsync ? + await CreateBatchOperation.RehydrateAsync(client, rehydrationToken) : + CreateBatchOperation.Rehydrate(client, rehydrationToken); + + static bool Validate(CreateBatchOperation operation) + { + BinaryData response = operation.GetRawResponse().Content; + using JsonDocument jsonDocument = JsonDocument.Parse(response); + + JsonElement idElement = jsonDocument.RootElement.GetProperty("id"); + JsonElement createdAtElement = jsonDocument.RootElement.GetProperty("created_at"); + JsonElement statusElement = jsonDocument.RootElement.GetProperty("status"); + JsonElement metadataElement = jsonDocument.RootElement.GetProperty("metadata"); + JsonElement testMetadataKeyElement = metadataElement.GetProperty("testMetadataKey"); + + string id = idElement.GetString(); + long createdAt = createdAtElement.GetInt64(); + string status = statusElement.GetString(); + string testMetadataKey = testMetadataKeyElement.GetString(); + + long unixTime2024 = (new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero)).ToUnixTimeSeconds(); + + Assert.That(id, Is.Not.Null.And.Not.Empty); + Assert.That(createdAt, Is.GreaterThan(unixTime2024)); + Assert.That(status, Is.EqualTo("validating")); + Assert.That(testMetadataKey, Is.EqualTo("test metadata value")); + + return true; + } + + Assert.IsTrue(Validate(batchOperation)); + Assert.IsTrue(Validate(rehydratedOperation)); + + // We don't test wait for completion live because this is documented to + // sometimes take 24 hours. + + Assert.AreEqual(batchOperation.HasCompleted, rehydratedOperation.HasCompleted); + + using JsonDocument originalOperationJson = JsonDocument.Parse(batchOperation.GetRawResponse().Content); + using JsonDocument rehydratedOperationJson = JsonDocument.Parse(rehydratedOperation.GetRawResponse().Content); - //var newBatchDynamic = batchResult.GetRawResponse().Content.ToDynamicFromJson(); - - //Assert.That(newBatchDynamic?.createdAt, Is.GreaterThan(new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero))); - //Assert.That(newBatchDynamic.status, Is.EqualTo("validating")); - //Assert.That(newBatchDynamic.metadata["testMetadataKey"], Is.EqualTo("test metadata value")); - //batchResult = await client.GetBatchAsync(newBatchDynamic.id, options: null); - //newBatchDynamic = batchResult.GetRawResponse().Content.ToObjectFromJson(); - //Assert.That(newBatchDynamic.endpoint, Is.EqualTo("/v1/chat/completions")); - //batchResult = await client.CancelBatchAsync(newBatchDynamic.id, options: null); - //newBatchDynamic = batchResult.GetRawResponse().Content.ToObjectFromJson(); - //Assert.That(newBatchDynamic.status, Is.EqualTo("cancelling")); + Assert.AreEqual(originalOperationJson.RootElement.GetProperty("id").GetString(), rehydratedOperationJson.RootElement.GetProperty("id").GetString()); + Assert.AreEqual(originalOperationJson.RootElement.GetProperty("created_at").GetInt64(), rehydratedOperationJson.RootElement.GetProperty("created_at").GetInt64()); + Assert.AreEqual(originalOperationJson.RootElement.GetProperty("status").GetString(), rehydratedOperationJson.RootElement.GetProperty("status").GetString()); } } \ No newline at end of file diff --git a/tests/Chat/ChatSmokeTests.cs b/tests/Chat/ChatSmokeTests.cs index c4824ace..79e9e487 100644 --- a/tests/Chat/ChatSmokeTests.cs +++ b/tests/Chat/ChatSmokeTests.cs @@ -11,6 +11,7 @@ using System.Reflection; using System.Text.Json; using System.Threading.Tasks; +using static System.Net.Mime.MediaTypeNames; namespace OpenAI.Tests.Chat; @@ -521,15 +522,200 @@ public void SerializeRefusalMessages() Assert.That(reserialized, Does.Contain("from a content part")); Assert.That(reserialized, Does.Contain("from the message refusal")); - AssistantChatMessage manufacturedMessage = new(toolCalls: []); + AssistantChatMessage manufacturedMessage = new([ + ChatToolCall.CreateFunctionToolCall("fake_tool_call_id", "fake_function_name", "{}") + ]); manufacturedMessage.Refusal = "No!"; string serialized = ModelReaderWriter.Write(manufacturedMessage).ToString(); Assert.That(serialized, Does.Contain("refusal")); Assert.That(serialized, Does.Contain("No!")); - Assert.That(serialized, Does.Not.Contain("tool")); + Assert.That(serialized, Does.Contain("tool_calls")); Assert.That(serialized, Does.Not.Contain("content")); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void SerializeChatMessageWithSingleStringContent(bool fromRawJson) + { + const string text = "Hello, world!"; + AssistantChatMessage message; + + if (fromRawJson) + { + BinaryData data = BinaryData.FromString($$""" + { + "role": "assistant", + "content": "{{text}}", + "additional_property": true + } + """); + + // We deserialize the raw JSON. Later, we serialize it back and confirm nothing was lost in the process. + message = ModelReaderWriter.Read(data); + } + else + { + // We construct a new instance. Later, we serialize it and confirm it was constructed correctly. + message = new AssistantChatMessage([ + ChatMessageContentPart.CreateTextPart(text), + ]); + } + + BinaryData serializedMessage = ModelReaderWriter.Write(message); + using JsonDocument messageAsJson = JsonDocument.Parse(serializedMessage); + Assert.That(messageAsJson.RootElement, Is.Not.Null); + Assert.That(messageAsJson.RootElement.ValueKind, Is.EqualTo(JsonValueKind.Object)); + + Assert.That(messageAsJson.RootElement.TryGetProperty("content", out JsonElement contentProperty), Is.True); + Assert.That(contentProperty, Is.Not.Null); + Assert.That(contentProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(contentProperty.ToString(), Is.EqualTo(text)); + + if (fromRawJson) + { + // Confirm that we also have the additional data. + Assert.That(messageAsJson.RootElement.TryGetProperty("additional_property", out JsonElement additionalPropertyProperty), Is.True); + Assert.That(additionalPropertyProperty, Is.Not.Null); + Assert.That(additionalPropertyProperty.ValueKind, Is.EqualTo(JsonValueKind.True)); + } + } + + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SerializeChatMessageWithEmptyStringContent(bool fromRawJson) + { + const string text = ""; + AssistantChatMessage message; + + if (fromRawJson) + { + BinaryData data = BinaryData.FromString($$""" + { + "role": "assistant", + "content": "{{text}}", + "additional_property": true + } + """); + + // We deserialize the raw JSON. Later, we serialize it back and confirm nothing was lost in the process. + message = ModelReaderWriter.Read(data); + } + else + { + // We construct a new instance. Later, we serialize it and confirm it was constructed correctly. + message = new AssistantChatMessage([ + ChatMessageContentPart.CreateTextPart(text), + ]); + } + + BinaryData serializedMessage = ModelReaderWriter.Write(message); + using JsonDocument messageAsJson = JsonDocument.Parse(serializedMessage); + Assert.That(messageAsJson.RootElement, Is.Not.Null); + Assert.That(messageAsJson.RootElement.ValueKind, Is.EqualTo(JsonValueKind.Object)); + + Assert.That(messageAsJson.RootElement.TryGetProperty("content", out JsonElement contentProperty), Is.True); + Assert.That(contentProperty, Is.Not.Null); + Assert.That(contentProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(contentProperty.ToString(), Is.EqualTo(text)); + + if (fromRawJson) + { + // Confirm that we also have the additional data. + Assert.That(messageAsJson.RootElement.TryGetProperty("additional_property", out JsonElement additionalPropertyProperty), Is.True); + Assert.That(additionalPropertyProperty, Is.Not.Null); + Assert.That(additionalPropertyProperty.ValueKind, Is.EqualTo(JsonValueKind.True)); + } + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void SerializeChatMessageWithNoContent(bool fromRawJson) + { + string toolCallId = "fake_tool_call_id"; + string toolCallType = "function"; + string toolCallFunctionName = "fake_function_name"; + string toolCallFunctionArguments = "{}"; + AssistantChatMessage message; + + if (fromRawJson) + { + BinaryData data = BinaryData.FromString($$""" + { + "role": "assistant", + "tool_calls": [{ + "id": "{{toolCallId}}", + "type": "{{toolCallType}}", + "function": { + "name": "{{toolCallFunctionName}}", + "arguments": "{{toolCallFunctionArguments}}" + } + }], + "additional_property": true + } + """); + + // We deserialize the raw JSON. Later, we serialize it back and confirm nothing was lost in the process. + message = ModelReaderWriter.Read(data); + } + else + { + // We construct a new instance. Later, we serialize it and confirm it was constructed correctly. + message = new AssistantChatMessage([ + ChatToolCall.CreateFunctionToolCall(toolCallId, toolCallFunctionName, "{}") + ]); + } + + BinaryData serializedMessage = ModelReaderWriter.Write(message); + using JsonDocument messageAsJson = JsonDocument.Parse(serializedMessage); + Assert.That(messageAsJson.RootElement, Is.Not.Null); + Assert.That(messageAsJson.RootElement.ValueKind, Is.EqualTo(JsonValueKind.Object)); + + Assert.That(messageAsJson.RootElement.TryGetProperty("content", out JsonElement contentProperty), Is.False); + + Assert.That(messageAsJson.RootElement.TryGetProperty("tool_calls", out JsonElement toolCallsProperty), Is.True); + Assert.That(toolCallsProperty, Is.Not.Null); + Assert.That(toolCallsProperty.ValueKind, Is.EqualTo(JsonValueKind.Array)); + + foreach (JsonElement toolCall in toolCallsProperty.EnumerateArray()) + { + Assert.That(toolCall.TryGetProperty("id", out JsonElement toolCallIdProperty), Is.True); + Assert.That(toolCallIdProperty, Is.Not.Null); + Assert.That(toolCallIdProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(toolCallIdProperty.ToString(), Is.EqualTo(toolCallId)); + + Assert.That(toolCall.TryGetProperty("type", out JsonElement toolCallTypeProperty), Is.True); + Assert.That(toolCallTypeProperty, Is.Not.Null); + Assert.That(toolCallTypeProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(toolCallTypeProperty.ToString(), Is.EqualTo(toolCallType)); + + Assert.That(toolCall.TryGetProperty("function", out JsonElement toolCallFunctionProperty), Is.True); + Assert.That(toolCallFunctionProperty, Is.Not.Null); + Assert.That(toolCallFunctionProperty.ValueKind, Is.EqualTo(JsonValueKind.Object)); + + Assert.That(toolCallFunctionProperty.TryGetProperty("name", out JsonElement toolCallFunctionNameProperty), Is.True); + Assert.That(toolCallFunctionNameProperty, Is.Not.Null); + Assert.That(toolCallFunctionNameProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(toolCallFunctionNameProperty.ToString(), Is.EqualTo(toolCallFunctionName)); + + Assert.That(toolCallFunctionProperty.TryGetProperty("arguments", out JsonElement toolCallFunctionArgumentsProperty), Is.True); + Assert.That(toolCallFunctionArgumentsProperty, Is.Not.Null); + Assert.That(toolCallFunctionArgumentsProperty.ValueKind, Is.EqualTo(JsonValueKind.String)); + Assert.That(toolCallFunctionArgumentsProperty.ToString(), Is.EqualTo(toolCallFunctionArguments)); + } + + if (fromRawJson) + { + // Confirm that we also have the additional data. + Assert.That(messageAsJson.RootElement.TryGetProperty("additional_property", out JsonElement additionalPropertyProperty), Is.True); + Assert.That(additionalPropertyProperty, Is.Not.Null); + Assert.That(additionalPropertyProperty.ValueKind, Is.EqualTo(JsonValueKind.True)); + } + } + #pragma warning disable CS0618 [Test] public void SerializeMessagesWithNullProperties() diff --git a/tests/Embeddings/EmbeddingsMockTests.cs b/tests/Embeddings/EmbeddingsMockTests.cs index 3f478a04..11ce456b 100644 --- a/tests/Embeddings/EmbeddingsMockTests.cs +++ b/tests/Embeddings/EmbeddingsMockTests.cs @@ -153,8 +153,8 @@ public async Task GenerateEmbeddingsWithIntegersDeserializesUsage() EmbeddingClient client = new EmbeddingClient("model", s_fakeCredential, clientOptions); OpenAIEmbeddingCollection embeddings = IsAsync - ? await client.GenerateEmbeddingsAsync([[1]]) - : client.GenerateEmbeddings([[1]]); + ? await client.GenerateEmbeddingsAsync([new[] { 1 }]) + : client.GenerateEmbeddings([new[] { 1 }]); Assert.That(embeddings.Usage.InputTokenCount, Is.EqualTo(10)); Assert.That(embeddings.Usage.TotalTokenCount, Is.EqualTo(20)); @@ -180,8 +180,8 @@ public async Task GenerateEmbeddingsWithIntegersDeserializesVector() EmbeddingClient client = new EmbeddingClient("model", s_fakeCredential, clientOptions); OpenAIEmbeddingCollection embeddings = IsAsync - ? await client.GenerateEmbeddingsAsync([[1]]) - : client.GenerateEmbeddings([[1]]); + ? await client.GenerateEmbeddingsAsync([new[] { 1 }]) + : client.GenerateEmbeddings([new[] { 1 }]); OpenAIEmbedding embedding = embeddings.Single(); float[] vector = embedding.ToFloats().ToArray(); @@ -197,12 +197,12 @@ public void GenerateEmbeddingsWithIntegersRespectsTheCancellationToken() if (IsAsync) { - Assert.That(async () => await client.GenerateEmbeddingsAsync([[1]], cancellationToken: cancellationSource.Token), + Assert.That(async () => await client.GenerateEmbeddingsAsync([new[] { 1 }], cancellationToken: cancellationSource.Token), Throws.InstanceOf()); } else { - Assert.That(() => client.GenerateEmbeddings([[1]], cancellationToken: cancellationSource.Token), + Assert.That(() => client.GenerateEmbeddings([new[] { 1 }], cancellationToken: cancellationSource.Token), Throws.InstanceOf()); } } diff --git a/tests/Embeddings/EmbeddingsTests.cs b/tests/Embeddings/EmbeddingsTests.cs index 02ab9d33..89540258 100644 --- a/tests/Embeddings/EmbeddingsTests.cs +++ b/tests/Embeddings/EmbeddingsTests.cs @@ -79,11 +79,11 @@ public async Task GenerateMultipleEmbeddings(EmbeddingsInputKind embeddingsInput } else if (embeddingsInputKind == EmbeddingsInputKind.UsingIntegers) { - List> prompts = + List> prompts = [ - [104, 101, 108, 108, 111], - [119, 111, 114, 108, 100], - [84, 69, 83, 84] + new[] {104, 101, 108, 108, 111 }, + new[] {119, 111, 114, 108, 100 }, + new[] {84, 69, 83, 84 } ]; embeddings = IsAsync @@ -162,8 +162,8 @@ public async Task GenerateMultipleEmbeddingsWithBadOptions(EmbeddingsInputKind e else if (embeddingsInputKind == EmbeddingsInputKind.UsingIntegers) { _ = IsAsync - ? await client.GenerateEmbeddingsAsync([[1]], options) - : client.GenerateEmbeddings([[1]], options); + ? await client.GenerateEmbeddingsAsync([new[] { 1 }], options) + : client.GenerateEmbeddings([new[] { 1 }], options); } } catch (Exception ex) @@ -175,6 +175,31 @@ public async Task GenerateMultipleEmbeddingsWithBadOptions(EmbeddingsInputKind e Assert.That(caughtException.Message, Contains.Substring("dimensions")); } + [Test] + public async Task EmbeddingFromStringAndEmbeddingFromTokenIdsAreEqual() + { + EmbeddingClient client = GetTestClient(); + + List input1 = new List { "Hello, world!" }; + List> input2 = new List> { new[] { 9906, 11, 1917, 0 } }; + + OpenAIEmbeddingCollection results1 = IsAsync + ? await client.GenerateEmbeddingsAsync(input1) + : client.GenerateEmbeddings(input1); + + OpenAIEmbeddingCollection results2 = IsAsync + ? await client.GenerateEmbeddingsAsync(input2) + : client.GenerateEmbeddings(input2); + + ReadOnlyMemory vector1 = results1[0].ToFloats(); + ReadOnlyMemory vector2 = results2[0].ToFloats(); + + for (int i = 0; i < vector1.Length; i++) + { + Assert.That(vector1.Span[i], Is.EqualTo(vector2.Span[i]).Within(0.0005)); + } + } + [Test] public void SerializeEmbeddingCollection() { diff --git a/tests/Files/FilesMockTests.cs b/tests/Files/FilesMockTests.cs index f8eb07d2..89d56cf0 100644 --- a/tests/Files/FilesMockTests.cs +++ b/tests/Files/FilesMockTests.cs @@ -35,21 +35,21 @@ public enum FileSourceKind private static object[] s_purposeSource = { - ("assistants", OpenAIFilePurpose.Assistants), - ("assistants_output", OpenAIFilePurpose.AssistantsOutput), - ("batch", OpenAIFilePurpose.Batch), - ("batch_output", OpenAIFilePurpose.BatchOutput), - ("fine-tune", OpenAIFilePurpose.FineTune), - ("fine-tune-results", OpenAIFilePurpose.FineTuneResults), - ("vision", OpenAIFilePurpose.Vision) + ("assistants", FilePurpose.Assistants), + ("assistants_output", FilePurpose.AssistantsOutput), + ("batch", FilePurpose.Batch), + ("batch_output", FilePurpose.BatchOutput), + ("fine-tune", FilePurpose.FineTune), + ("fine-tune-results", FilePurpose.FineTuneResults), + ("vision", FilePurpose.Vision) }; #pragma warning disable CS0618 private static object[] s_statusSource = { - ("uploaded", OpenAIFileStatus.Uploaded), - ("processed", OpenAIFileStatus.Processed), - ("error", OpenAIFileStatus.Error) + ("uploaded", FileStatus.Uploaded), + ("processed", FileStatus.Processed), + ("error", FileStatus.Error) }; #pragma warning restore CS0618 @@ -89,7 +89,7 @@ public async Task GetFileDeserializesCreatedAt() [Test] [TestCaseSource(nameof(s_purposeSource))] - public async Task GetFileDeserializesPurpose((string stringValue, OpenAIFilePurpose expectedValue) purpose) + public async Task GetFileDeserializesPurpose((string stringValue, FilePurpose expectedValue) purpose) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -109,7 +109,7 @@ public async Task GetFileDeserializesPurpose((string stringValue, OpenAIFilePurp #pragma warning disable CS0618 [Test] [TestCaseSource(nameof(s_statusSource))] - public async Task GetFileDeserializesStatus((string stringValue, OpenAIFileStatus expectedValue) status) + public async Task GetFileDeserializesStatus((string stringValue, FileStatus expectedValue) status) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -195,7 +195,7 @@ public async Task UploadFileDeserializesCreatedAt(FileSourceKind fileSourceKind) [Test] public async Task UploadFileDeserializesPurpose( [ValueSource(nameof(s_fileSourceKindSource))] FileSourceKind fileSourceKind, - [ValueSource(nameof(s_purposeSource))] (string stringValue, OpenAIFilePurpose expectedValue) purpose) + [ValueSource(nameof(s_purposeSource))] (string stringValue, FilePurpose expectedValue) purpose) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -211,7 +211,7 @@ public async Task UploadFileDeserializesPurpose( [Test] public async Task UploadFileDeserializesStatus( [ValueSource(nameof(s_fileSourceKindSource))] FileSourceKind fileSourceKind, - [ValueSource(nameof(s_statusSource))] (string stringValue, OpenAIFileStatus expectedValue) status) + [ValueSource(nameof(s_statusSource))] (string stringValue, FileStatus expectedValue) status) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -275,8 +275,8 @@ public async Task GetFilesDeserializesId() FileClient client = new FileClient(s_fakeCredential, clientOptions); OpenAIFileCollection fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); OpenAIFile fileInfo = fileInfoCollection.Single(); Assert.That(fileInfo.Id, Is.EqualTo("returned_file_id")); @@ -297,8 +297,8 @@ public async Task GetFilesDeserializesCreatedAt() FileClient client = new FileClient(s_fakeCredential, clientOptions); OpenAIFileCollection fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); OpenAIFile fileInfo = fileInfoCollection.Single(); Assert.That(fileInfo.CreatedAt.ToUnixTimeSeconds(), Is.EqualTo(1704096000)); @@ -306,7 +306,7 @@ public async Task GetFilesDeserializesCreatedAt() [Test] [TestCaseSource(nameof(s_purposeSource))] - public async Task GetFilesDeserializesPurpose((string stringValue, OpenAIFilePurpose expectedValue) purpose) + public async Task GetFilesDeserializesPurpose((string stringValue, FilePurpose expectedValue) purpose) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -320,8 +320,8 @@ public async Task GetFilesDeserializesPurpose((string stringValue, OpenAIFilePur FileClient client = new FileClient(s_fakeCredential, clientOptions); OpenAIFileCollection fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); OpenAIFile fileInfo = fileInfoCollection.Single(); Assert.That(fileInfo.Purpose, Is.EqualTo(purpose.expectedValue)); @@ -330,7 +330,7 @@ public async Task GetFilesDeserializesPurpose((string stringValue, OpenAIFilePur #pragma warning disable CS0618 [Test] [TestCaseSource(nameof(s_statusSource))] - public async Task GetFilesDeserializesStatus((string stringValue, OpenAIFileStatus expectedValue) status) + public async Task GetFilesDeserializesStatus((string stringValue, FileStatus expectedValue) status) { OpenAIClientOptions clientOptions = GetClientOptionsWithMockResponse(200, $$""" { @@ -344,8 +344,8 @@ public async Task GetFilesDeserializesStatus((string stringValue, OpenAIFileStat FileClient client = new FileClient(s_fakeCredential, clientOptions); OpenAIFileCollection fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); OpenAIFile fileInfo = fileInfoCollection.Single(); Assert.That(fileInfo.Status, Is.EqualTo(status.expectedValue)); @@ -368,8 +368,8 @@ public async Task GetFilesDeserializesStatusDetails() FileClient client = new FileClient(s_fakeCredential, clientOptions); OpenAIFileCollection fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); OpenAIFile fileInfo = fileInfoCollection.Single(); Assert.That(fileInfo.StatusDetails, Is.EqualTo("This is definitely an error.")); @@ -385,12 +385,12 @@ public void GetFilesRespectsTheCancellationToken() if (IsAsync) { - Assert.That(async () => await client.GetFilesAsync(OpenAIFilePurpose.Assistants, cancellationSource.Token), + Assert.That(async () => await client.GetFilesAsync(FilePurpose.Assistants, cancellationSource.Token), Throws.InstanceOf()); } else { - Assert.That(() => client.GetFiles(OpenAIFilePurpose.Assistants, cancellationSource.Token), + Assert.That(() => client.GetFiles(FilePurpose.Assistants, cancellationSource.Token), Throws.InstanceOf()); } } diff --git a/tests/Files/FilesTests.cs b/tests/Files/FilesTests.cs index cd57f4b0..c92ff016 100644 --- a/tests/Files/FilesTests.cs +++ b/tests/Files/FilesTests.cs @@ -44,8 +44,8 @@ public async Task ListFiles() uploadedVisionFile = await client.UploadFileAsync(visionFilePath, FileUploadPurpose.Vision); fileInfoCollection = IsAsync - ? await client.GetFilesAsync(OpenAIFilePurpose.Assistants) - : client.GetFiles(OpenAIFilePurpose.Assistants); + ? await client.GetFilesAsync(FilePurpose.Assistants) + : client.GetFiles(FilePurpose.Assistants); } finally { @@ -108,25 +108,6 @@ public async Task ListFiles() Assert.That(visionFileInfo, Is.Null); } - [Test] - public void ListFilesCanParseServiceError() - { - FileClient client = GetTestClient(); - OpenAIFilePurpose fakePurpose = new OpenAIFilePurpose("world_domination"); - ClientResultException ex = null; - - if (IsAsync) - { - ex = Assert.ThrowsAsync(async () => await client.GetFilesAsync(fakePurpose)); - } - else - { - ex = Assert.Throws(() => client.GetFiles(fakePurpose)); - } - - Assert.That(ex.Status, Is.EqualTo(400)); - } - public enum FileSourceKind { UsingStream, @@ -192,9 +173,9 @@ public async Task UploadFile(FileSourceKind fileSourceKind) Assert.That(fileInfo.SizeInBytes, Is.EqualTo(expectedSize)); Assert.That(fileInfo.CreatedAt.ToUnixTimeSeconds(), Is.GreaterThan(unixTime2024)); Assert.That(fileInfo.Filename, Is.EqualTo(expectedFilename)); - Assert.That(fileInfo.Purpose, Is.EqualTo(OpenAIFilePurpose.Vision)); + Assert.That(fileInfo.Purpose, Is.EqualTo(FilePurpose.Vision)); #pragma warning disable CS0618 - Assert.That(fileInfo.Status, Is.Not.EqualTo(default(OpenAIFileStatus))); + Assert.That(fileInfo.Status, Is.Not.EqualTo(default(FileStatus))); #pragma warning restore CS0618 } diff --git a/tests/Files/OpenAIFilesModelFactoryTests.cs b/tests/Files/OpenAIFilesModelFactoryTests.cs index c6f94a93..fc4d6ead 100644 --- a/tests/Files/OpenAIFilesModelFactoryTests.cs +++ b/tests/Files/OpenAIFilesModelFactoryTests.cs @@ -50,8 +50,8 @@ public void OpenAIFileInfoWithNoPropertiesWorks() Assert.That(openAIFileInfo.SizeInBytes, Is.Null); Assert.That(openAIFileInfo.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFileInfo.Filename, Is.Null); - Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFileInfo.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFileInfo.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFileInfo.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -67,8 +67,8 @@ public void OpenAIFileInfoWithIdWorks() Assert.That(openAIFileInfo.SizeInBytes, Is.Null); Assert.That(openAIFileInfo.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFileInfo.Filename, Is.Null); - Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFileInfo.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFileInfo.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFileInfo.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -84,8 +84,8 @@ public void OpenAIFileInfoWithSizeInBytesWorks() Assert.That(openAIFile.SizeInBytes, Is.EqualTo(sizeInBytes)); Assert.That(openAIFile.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFile.Filename, Is.Null); - Assert.That(openAIFile.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFile.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFile.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFile.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFile.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -101,8 +101,8 @@ public void OpenAIFileInfoWithCreatedAtWorks() Assert.That(openAIFileInfo.SizeInBytes, Is.Null); Assert.That(openAIFileInfo.CreatedAt, Is.EqualTo(createdAt)); Assert.That(openAIFileInfo.Filename, Is.Null); - Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFileInfo.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFileInfo.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFileInfo.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFileInfo.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -118,8 +118,8 @@ public void OpenAIFileInfoWithFilenameWorks() Assert.That(openAIFile.SizeInBytes, Is.Null); Assert.That(openAIFile.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFile.Filename, Is.EqualTo(filename)); - Assert.That(openAIFile.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFile.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFile.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFile.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFile.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -128,7 +128,7 @@ public void OpenAIFileInfoWithFilenameWorks() [Test] public void OpenAIFileInfoWithPurposeWorks() { - OpenAIFilePurpose purpose = OpenAIFilePurpose.Vision; + FilePurpose purpose = FilePurpose.Vision; OpenAIFile openAIFile = OpenAIFilesModelFactory.OpenAIFileInfo(purpose: purpose); Assert.That(openAIFile.Id, Is.Null); @@ -136,7 +136,7 @@ public void OpenAIFileInfoWithPurposeWorks() Assert.That(openAIFile.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFile.Filename, Is.Null); Assert.That(openAIFile.Purpose, Is.EqualTo(purpose)); - Assert.That(openAIFile.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFile.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFile.StatusDetails, Is.Null); } #pragma warning restore CS0618 @@ -145,14 +145,14 @@ public void OpenAIFileInfoWithPurposeWorks() [Test] public void OpenAIFileInfoWithStatusWorks() { - OpenAIFileStatus status = OpenAIFileStatus.Uploaded; + FileStatus status = FileStatus.Uploaded; OpenAIFile openAIFile = OpenAIFilesModelFactory.OpenAIFileInfo(status: status); Assert.That(openAIFile.Id, Is.Null); Assert.That(openAIFile.SizeInBytes, Is.Null); Assert.That(openAIFile.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFile.Filename, Is.Null); - Assert.That(openAIFile.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); + Assert.That(openAIFile.Purpose, Is.EqualTo(default(FilePurpose))); Assert.That(openAIFile.Status, Is.EqualTo(status)); Assert.That(openAIFile.StatusDetails, Is.Null); } @@ -169,8 +169,8 @@ public void OpenAIFileInfoWithStatusDetailsWorks() Assert.That(openAIFile.SizeInBytes, Is.Null); Assert.That(openAIFile.CreatedAt, Is.EqualTo(default(DateTimeOffset))); Assert.That(openAIFile.Filename, Is.Null); - Assert.That(openAIFile.Purpose, Is.EqualTo(default(OpenAIFilePurpose))); - Assert.That(openAIFile.Status, Is.EqualTo(default(OpenAIFileStatus))); + Assert.That(openAIFile.Purpose, Is.EqualTo(default(FilePurpose))); + Assert.That(openAIFile.Status, Is.EqualTo(default(FileStatus))); Assert.That(openAIFile.StatusDetails, Is.EqualTo(statusDetails)); } #pragma warning restore CS0618 diff --git a/tests/FineTuning/FineTuningTests.cs b/tests/FineTuning/FineTuningTests.cs index 678c9fa4..2a200907 100644 --- a/tests/FineTuning/FineTuningTests.cs +++ b/tests/FineTuning/FineTuningTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using OpenAI.Files; using OpenAI.FineTuning; using OpenAI.Tests.Utility; using System; @@ -34,11 +35,11 @@ public void CreateJobCanParseServiceError() if (IsAsync) { - ex = Assert.ThrowsAsync(async () => await client.CreateJobAsync(BinaryContent.Create(data))); + ex = Assert.ThrowsAsync(async () => await client.CreateFineTuningJobAsync(BinaryContent.Create(data), waitUntilCompleted: false)); } else { - ex = Assert.Throws(() => client.CreateJob(BinaryContent.Create(data))); + ex = Assert.Throws(() => client.CreateFineTuningJob(BinaryContent.Create(data), waitUntilCompleted: false)); } Assert.That(ex.Status, Is.EqualTo(400)); @@ -99,77 +100,28 @@ public void GetJobsWorks() } } - [Test] - public void CancelJobCanParseServiceError() - { - FineTuningClient client = GetTestClient(TestScenario.FineTuning); - ClientResultException ex = null; - - if (IsAsync) - { - ex = Assert.ThrowsAsync(async () => await client.CancelJobAsync("fakeJobId", options: null)); - } - else - { - ex = Assert.Throws(() => client.CancelJob("fakeJobId", options: null)); - } - - Assert.That(ex.Status, Is.EqualTo(404)); - } - - [Test] - public void GetJobEventsAsyncCanParseServiceError() - { - AssertAsyncOnly(); - - FineTuningClient client = GetTestClient(TestScenario.FineTuning); - IAsyncEnumerable enumerable = client.GetJobEventsAsync("fakeJobId", after: null, limit: null, options: null).GetRawPagesAsync(); - IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator(); - - ClientResultException ex = Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync()); - - Assert.That(ex.Status, Is.EqualTo(404)); - } - - [Test] - public void GetJobEventsCanParseServiceError() - { - AssertSyncOnly(); - - FineTuningClient client = GetTestClient(TestScenario.FineTuning); - IEnumerable enumerable = client.GetJobEvents("fakeJobId", after: null, limit: null, options: null).GetRawPages(); - IEnumerator enumerator = enumerable.GetEnumerator(); - - ClientResultException ex = Assert.Throws(() => enumerator.MoveNext()); - - Assert.That(ex.Status, Is.EqualTo(404)); - } - - [Test] - public void GetJobCheckpointsAsyncCanParseServiceError() - { - AssertAsyncOnly(); - - FineTuningClient client = GetTestClient(TestScenario.FineTuning); - IAsyncEnumerable enumerable = client.GetJobCheckpointsAsync("fakeJobId", after: null, limit: null, options: null).GetRawPagesAsync(); - IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator(); - - ClientResultException ex = Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync()); - - Assert.That(ex.Status, Is.EqualTo(404)); - } - - [Test] - public void GetJobCheckpointsCanParseServiceError() - { - AssertSyncOnly(); - - FineTuningClient client = GetTestClient(TestScenario.FineTuning); - IEnumerable enumerable = client.GetJobCheckpoints("fakeJobId", after: null, limit: null, options: null).GetRawPages(); - IEnumerator enumerator = enumerable.GetEnumerator(); - - ClientResultException ex = Assert.Throws(() => enumerator.MoveNext()); - - Assert.That(ex.Status, Is.EqualTo(404)); - } + // We need to add this test back once we have access to the test resources + // + //[Test] + //public void BasicFineTuningJobOperationsWork() + //{ + // // Upload training file first + // FileClient fileClient = GetTestClient(TestScenario.Files); + // string filename = "toy_chat.jsonl"; + // BinaryData fileContent = BinaryData.FromString(""" + // {"messages": [{"role": "user", "content": "I lost my book today."}, {"role": "assistant", "content": "You can read everything on ebooks these days!"}]} + // {"messages": [{"role": "system", "content": "You are a happy assistant that puts a positive spin on everything."}, {"role": "assistant", "content": "You're great!"}]} + // """); + // OpenAIFile uploadedFile = fileClient.UploadFile(fileContent, filename, FileUploadPurpose.FineTune); + // Assert.That(uploadedFile?.Filename, Is.EqualTo(filename)); + + // // Submit fine-tuning job + // FineTuningClient client = GetTestClient(TestScenario.FineTuning); + + // string json = $"{{\"training_file\":\"{uploadedFile.Id}\",\"model\":\"gpt-3.5-turbo\"}}"; + // BinaryData input = BinaryData.FromString(json); + // using BinaryContent content = BinaryContent.Create(input); + + // FineTuningJobOperation operation = client.CreateJob(content, waitUntilCompleted: false); + //} } diff --git a/tests/Models/ModelsTests.cs b/tests/Models/ModelsTests.cs index 7d0794f5..195dc0ca 100644 --- a/tests/Models/ModelsTests.cs +++ b/tests/Models/ModelsTests.cs @@ -94,7 +94,8 @@ public void DeleteModelCanParseServiceError() // If the model exists but the user doesn't own it, the service returns 403. // If the model doesn't exist at all, the service returns 404. - Assert.That(ex.Status, Is.EqualTo(404)); + // The service has changed the behavior in the past. + Assert.That((ex.Status == 403 || ex.Status == 404), Is.True); } [Test] diff --git a/tests/UserAgentTests.cs b/tests/UserAgentTests.cs index 238c9925..802e958c 100644 --- a/tests/UserAgentTests.cs +++ b/tests/UserAgentTests.cs @@ -30,7 +30,7 @@ private void UserAgentStringWorks(bool useApplicationId) } : new(); options.AddPolicy(policy, PipelinePosition.BeforeTransport); - ChatClient client = new("no-real-model-needed", Environment.GetEnvironmentVariable("OPENAI_API_KEY"), options); + ChatClient client = new("no-real-model-needed", new ApiKeyCredential(Environment.GetEnvironmentVariable("OPENAI_API_KEY")), options); RequestOptions noThrowOptions = new() { ErrorOptions = ClientErrorBehaviors.NoThrow, }; using BinaryContent emptyContent = BinaryContent.Create(new MemoryStream()); _ = client.CompleteChat(emptyContent, noThrowOptions);