Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release History

## 2.7.0 (Unreleased)
## 2.7.0 (2025-11-13)

### Acknowledgments

Expand Down
67 changes: 67 additions & 0 deletions api/OpenAI.net8.0.cs

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions api/OpenAI.netstandard2.0.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/Test-ApiCompatibility.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,5 @@ $experimentalNamespaces = @(
Invoke-APICompat -ProjectPath $projectPath `
-ReleasePath $releasePath `
-PackageName "OpenAI" `
-BaselineVersion "2.5.0" `
-BaselineVersion "2.6.0" `
-IgnoredNamespaces $experimentalNamespaces
2 changes: 1 addition & 1 deletion src/OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Copyright>Copyright (c) 2024 OpenAI (https://openai.com)</Copyright>

<VersionPrefix>2.6.0</VersionPrefix>
<VersionPrefix>2.7.0</VersionPrefix>
<VersionSuffix></VersionSuffix>

<TargetFrameworks>net8.0;netstandard2.0</TargetFrameworks>
Expand Down
17 changes: 13 additions & 4 deletions tests/Assistants/Assistants.VectorStoresTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ public async Task CanCreateGetAndDeleteVectorStores()
public async Task CanEnumerateVectorStores()
{
VectorStoreClient client = GetTestClient();
for (int i = 0; i < 5; i++)

int vectorStoreCount = 3;

for (int i = 0; i < vectorStoreCount; i++)
{
VectorStore vectorStore = await client.CreateVectorStoreAsync(
new VectorStoreCreationOptions()
Expand All @@ -122,7 +125,7 @@ public async Task CanEnumerateVectorStores()

if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(5000);
await Task.Delay(TimeSpan.FromSeconds(10));
}

int lastIdSeen = int.MaxValue;
Expand Down Expand Up @@ -155,7 +158,9 @@ public async Task CanAssociateFiles()
VectorStore vectorStore = await client.CreateVectorStoreAsync();
Validate(vectorStore);

IReadOnlyList<OpenAIFile> files = await GetNewTestFiles(3);
int fileCount = 3;

IReadOnlyList<OpenAIFile> files = await GetNewTestFiles(fileCount);

foreach (OpenAIFile file in files)
{
Expand All @@ -170,6 +175,10 @@ public async Task CanAssociateFiles()
Assert.That(vectorStoreFile.Status, Is.EqualTo(VectorStoreFileStatus.InProgress));
});
}
if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(TimeSpan.FromSeconds(10));
}

FileFromStoreRemovalResult removalResult = await client.RemoveFileFromVectorStoreAsync(vectorStore.Id, files[0].Id);
Assert.That(removalResult.FileId, Is.EqualTo(files[0].Id));
Expand All @@ -189,7 +198,7 @@ public async Task CanAssociateFiles()
Assert.That(vectorStoreFile.FileId, Is.Not.EqualTo(files[0].Id));
Assert.That(vectorStoreFile.VectorStoreId, Is.EqualTo(vectorStore.Id));
}
Assert.That(count, Is.EqualTo(2));
Assert.That(count, Is.EqualTo(fileCount - 1));
}

[RecordedTest]
Expand Down
19 changes: 17 additions & 2 deletions tests/Assistants/AssistantsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,11 @@ public async Task Pagination_CanEnumerateAssistantsAsync()
Assert.That(assistant.Name, Is.EqualTo($"Test Assistant {i}"));
}

if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(TimeSpan.FromSeconds(10));
}

// Page through collection
int count = 0;
AsyncCollectionResult<Assistant> assistants = client.GetAssistantsAsync(new AssistantCollectionOptions() { Order = AssistantCollectionOrder.Descending });
Expand Down Expand Up @@ -1031,7 +1036,7 @@ public async Task Pagination_CanEnumerateAssistantsAsync()
[RecordedTest]
public async Task Pagination_CanPageThroughAssistantCollection()
{
const int TestAssistantCount = 10;
const int TestAssistantCount = 5;
const int TestPageSizeLimit = 2;

AssistantClient client = GetTestClient();
Expand All @@ -1047,6 +1052,11 @@ public async Task Pagination_CanPageThroughAssistantCollection()
Assert.That(assistant.Name, Is.EqualTo($"Test Assistant {i}"));
}

if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(TimeSpan.FromSeconds(10));
}

// Page through collection
int count = 0;
int pageCount = 0;
Expand Down Expand Up @@ -1096,7 +1106,7 @@ private static IEnumerable<Assistant> GetAssistantsFromPage(ClientResult page)
[RecordedTest]
public async Task Pagination_CanRehydrateAssistantPageCollectionFromBytes()
{
const int TestAssistantCount = 10;
const int TestAssistantCount = 5;
const int TestPageSizeLimit = 2;

AssistantClient client = GetTestClient();
Expand All @@ -1112,6 +1122,11 @@ public async Task Pagination_CanRehydrateAssistantPageCollectionFromBytes()
Assert.That(assistant.Name, Is.EqualTo($"Test Assistant {i}"));
}

if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(TimeSpan.FromSeconds(10));
}

AsyncCollectionResult<Assistant> assistants = client.GetAssistantsAsync(
new AssistantCollectionOptions()
{
Expand Down
108 changes: 73 additions & 35 deletions tests/Responses/ResponsesToolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -810,26 +810,38 @@ in client.CreateResponseStreamingAsync(message, responseOptions))
}

[RecordedTest]
[LiveOnly(Reason = "Temp due to the recording framework timing out")]
public async Task ImageGenToolInputMaskWithImageBytes()
{
OpenAIResponseClient client = GetTestClient();
OpenAIResponseClient client = GetTestClient(options: new() { NetworkTimeout = TimeSpan.FromMinutes(5) });

string imageFilename = "images_dog_and_cat.png";
string imageFilename = "images_empty_room.png";
string imagePath = Path.Combine("Assets", imageFilename);
BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath));

string maskFilename = "images_empty_room_with_mask.png";
string maskPath = Path.Combine("Assets", maskFilename);
BinaryData maskBytes = BinaryData.FromBytes(File.ReadAllBytes(maskPath));

List<ResponseItem> inputItems = [
ResponseItem.CreateUserMessageItem("Edit this image by adding a big cat with big round eyes and large cat ears, sitting in an empty room and looking at the camera."),
ResponseItem.CreateUserMessageItem([ResponseContentPart.CreateInputImagePart(imageBytes, "image/png")])
];

ResponseCreationOptions options = new()
{
Tools =
{
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
inputImageMask: new(BinaryData.FromBytes(File.ReadAllBytes(imagePath)), "image/png"))
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1-mini",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
size: ImageGenerationToolSize.W1024xH1024,
quality: ImageGenerationToolQuality.Low,
inputImageMask: new(maskBytes, "image/png"))
}
};

OpenAIResponse response = await client.CreateResponseAsync(
"Generate an image of gray tabby cat hugging an otter with an orange scarf",
options);
OpenAIResponse response = await client.CreateResponseAsync(inputItems, options);

Assert.That(response.OutputItems, Has.Count.EqualTo(2));
Assert.That(response.OutputItems[0], Is.InstanceOf<ImageGenerationCallResponseItem>());
Expand All @@ -849,22 +861,30 @@ public async Task ImageGenToolInputMaskWithImageBytes()
[RecordedTest]
public async Task ImageGenToolInputMaskWithImageUri()
{
OpenAIResponseClient client = GetTestClient();
OpenAIResponseClient client = GetTestClient(options: new() { NetworkTimeout = TimeSpan.FromMinutes(5) });

Uri imageUri = new("https://github.com/openai/openai-dotnet/blob/db6328accdd7927f19915cdc5412eb841f2447c1/tests/Assets/images_empty_room.png?raw=true");
Uri maskUri = new("https://github.com/openai/openai-dotnet/blob/db6328accdd7927f19915cdc5412eb841f2447c1/tests/Assets/images_empty_room_with_mask.png?raw=true");

List<ResponseItem> inputItems = [
ResponseItem.CreateUserMessageItem("Edit this image by adding a big cat with big round eyes and large cat ears, sitting in an empty room and looking at the camera."),
ResponseItem.CreateUserMessageItem([ResponseContentPart.CreateInputImagePart(imageUri)])
];

ResponseCreationOptions options = new()
{
Tools =
{
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
inputImageMask: new(imageUri: new Uri("https://upload.wikimedia.org/wikipedia/commons/c/c3/Openai.png")))
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1-mini",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
size: ImageGenerationToolSize.W1024xH1024,
quality: ImageGenerationToolQuality.Low,
inputImageMask: new(maskUri))
}
};

OpenAIResponse response = await client.CreateResponseAsync(
"Generate an image of gray tabby cat hugging an otter with an orange scarf",
options);
OpenAIResponse response = await client.CreateResponseAsync(inputItems, options);

Assert.That(response.OutputItems, Has.Count.EqualTo(2));
Assert.That(response.OutputItems[0], Is.InstanceOf<ImageGenerationCallResponseItem>());
Expand All @@ -882,41 +902,59 @@ public async Task ImageGenToolInputMaskWithImageUri()
}

[RecordedTest]
[Category("MPFD")]
public async Task ImageGenToolInputMaskWithFileId()
{
OpenAIResponseClient client = GetTestClient();
OpenAIResponseClient client = GetTestClient(options: new() { NetworkTimeout = TimeSpan.FromMinutes(5) });

OpenAIFileClient fileClient = GetProxiedOpenAIClient<OpenAIFileClient>(TestScenario.Files);

string imageFilename = "images_dog_and_cat.png";
string imageFilename = "images_empty_room.png";
string imagePath = Path.Combine("Assets", imageFilename);
using Stream image = File.OpenRead(imagePath);
BinaryData imageData = BinaryData.FromStream(image);
BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath));

string maskFilename = "images_empty_room_with_mask.png";
string maskPath = Path.Combine("Assets", maskFilename);
BinaryData maskBytes = BinaryData.FromBytes(File.ReadAllBytes(maskPath));

OpenAIFile file;
OpenAIFile imageFile;
using (Recording.DisableRequestBodyRecording()) // Temp pending https://github.com/Azure/azure-sdk-tools/issues/11901
{
file = await fileClient.UploadFileAsync(
imageData,
imageFilename,
FileUploadPurpose.UserData);
imageFile = await fileClient.UploadFileAsync(imageBytes, imageFilename, FileUploadPurpose.UserData);
}
Validate(imageFile);

OpenAIFile maskFile;
using (Recording.DisableRequestBodyRecording()) // Temp pending https://github.com/Azure/azure-sdk-tools/issues/11901
{
maskFile = await fileClient.UploadFileAsync(maskBytes, maskFilename, FileUploadPurpose.UserData);
}
Validate(imageFile);

if (Mode != RecordedTestMode.Playback)
{
await Task.Delay(TimeSpan.FromSeconds(10));
}
Validate(file);

List<ResponseItem> inputItems = [
ResponseItem.CreateUserMessageItem("Edit this image by adding a big cat with big round eyes and large cat ears, sitting in an empty room and looking at the camera."),
ResponseItem.CreateUserMessageItem([ResponseContentPart.CreateInputImagePart(imageFileId: imageFile.Id)])
];

ResponseCreationOptions options = new()
{
Tools =
{
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
inputImageMask: new(fileId: file.Id))
ResponseTool.CreateImageGenerationTool(
model: "gpt-image-1-mini",
outputFileFormat: ImageGenerationToolOutputFileFormat.Png,
size: ImageGenerationToolSize.W1024xH1024,
quality: ImageGenerationToolQuality.Low,
inputImageMask: new(fileId: maskFile.Id))
}
};

OpenAIResponse response = await client.CreateResponseAsync(
"Generate an image of gray tabby cat hugging an otter with an orange scarf",
options);
OpenAIResponse response = await client.CreateResponseAsync(inputItems, options);

Assert.That(response.OutputItems, Has.Count.EqualTo(2));
Assert.That(response.OutputItems[0], Is.InstanceOf<ImageGenerationCallResponseItem>());
Expand Down Expand Up @@ -1003,5 +1041,5 @@ private static void ValidateCodeInterpreterEvent(ref int inProgressCount, ref in
}
}

private OpenAIResponseClient GetTestClient(string overrideModel = null) => GetProxiedOpenAIClient<OpenAIResponseClient>(TestScenario.Responses, overrideModel);
private OpenAIResponseClient GetTestClient(string overrideModel = null, OpenAIClientOptions options = null) => GetProxiedOpenAIClient<OpenAIResponseClient>(TestScenario.Responses, overrideModel, options: options);
}
Loading
Loading