Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 2 additions & 8 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,18 @@
-->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<!-- General Dependencies -->
<ItemGroup>
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="System.ClientModel" Version="1.10.0" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.3" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.2" />
</ItemGroup>

<!-- Build-only Dependencies -->
<ItemGroup>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.102" PrivateAssets="All" />
<PackageVersion Include="Microsoft.DotNet.GenAPI.Task" Version="10.0.300-preview.0.26072.115" PrivateAssets="All" />
</ItemGroup>

<!-- Test-Only Dependencies -->
<ItemGroup Condition="'$(IsTestProject)' == 'true'">
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
Expand All @@ -28,22 +26,18 @@
<PackageVersion Include="NUnit" Version="4.4.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="10.0.2" />

<!--IMPORTANT: The Moq version should not be updated without legal team approval -->
<PackageVersion Include="Moq" Version="[4.18.2]" />

<!-- Development Feed Packages -->
<PackageVersion Include="Microsoft.ClientModel.TestFramework" Version="1.0.0-alpha.20260408.1" />
</ItemGroup>

<!-- Generator-Only Dependencies -->
<ItemGroup Condition="'$(IsGeneratorProject)' == 'true' or $(MSBuildProjectName.EndsWith('Plugin'))">
<PackageVersion Include="Microsoft.TypeSpec.Generator.ClientModel" Version="1.0.0-alpha.20260501.5" />
</ItemGroup>

<!-- Transitive Dependency Overrides: https://github.com/advisories/GHSA-g4vj-cjjj-v7hg -->
<ItemGroup>
<PackageVersion Include="NuGet.Packaging" Version="6.14.3" />
<PackageVersion Include="NuGet.Protocol" Version="6.14.3" />
</ItemGroup>
</Project>
</Project>
6 changes: 4 additions & 2 deletions OpenAI/src/Custom/Assistants/MessageCreationAttachment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.IO;

namespace OpenAI.Assistants;

[CodeGenType("CreateMessageRequestAttachment")]
[CodeGenSerialization(nameof(Tools), "tools", SerializationValueHook = nameof(SerializeTools), DeserializationValueHook = nameof(DeserializeTools))]
public partial class MessageCreationAttachment
{

/// <summary>
/// The tools to which the attachment applies to.
/// </summary>
Expand All @@ -35,8 +37,8 @@ private void SerializeTools(Utf8JsonWriter writer, ModelReaderWriterOptions opti
writer.WriteStartArray();
foreach (ToolDefinition tool in Tools)
{
using var ms = new System.IO.MemoryStream();
using (var tempWriter = new Utf8JsonWriter(ms))
using Microsoft.IO.RecyclableMemoryStream ms = OpenAI.MemoryStreamManager.Manager.GetStream();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using Microsoft.IO.RecyclableMemoryStream ms = OpenAI.MemoryStreamManager.Manager.GetStream();
using var ms = OpenAI.MemoryStreamManager.Manager.GetStream();

nit: Please preserve the existing form.

using (var tempWriter = new Utf8JsonWriter(ms as System.IO.Stream))
Comment on lines +40 to +41

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not interested in the potential null outcome and an NRE would be confusing, let's please use a direct cast.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I will fix that.

{
tempWriter.WriteObjectValue(tool, options);
tempWriter.Flush();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Buffers;
using System.ClientModel;
using System.Diagnostics;
using System.IO;
using System.Text.Json;

#nullable enable
Expand All @@ -19,8 +19,8 @@ public CreateBatchOperationToken(string batchId)

public override BinaryData ToBytes()
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using var stream = OpenAI.MemoryStreamManager.Manager.GetStream();

using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);
writer.WriteStartObject();

writer.WriteString("batchId", BatchId);
Expand Down
13 changes: 7 additions & 6 deletions OpenAI/src/Custom/Embeddings/EmbeddingClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.TypeSpec.Generator.Customizations;
using System;
using System.Buffers;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
Expand Down Expand Up @@ -254,8 +255,8 @@ public virtual ClientResult<OpenAIEmbeddingCollection> GenerateEmbeddings(IEnume

private void CreateEmbeddingGenerationOptions(string input, ref EmbeddingGenerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStringValue(input);
writer.Flush();
Expand All @@ -267,8 +268,8 @@ private void CreateEmbeddingGenerationOptions(string input, ref EmbeddingGenerat

private void CreateEmbeddingGenerationOptions(IEnumerable<string> inputs, ref EmbeddingGenerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartArray();

Expand All @@ -287,8 +288,8 @@ private void CreateEmbeddingGenerationOptions(IEnumerable<string> inputs, ref Em

private void CreateEmbeddingGenerationOptions(IEnumerable<ReadOnlyMemory<int>> inputs, ref EmbeddingGenerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartArray();

Expand Down
5 changes: 3 additions & 2 deletions OpenAI/src/Custom/FineTuning/Internal/FineTuningJobToken.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.ClientModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -21,8 +22,8 @@ public FineTuningJobToken(string jobId)

public override BinaryData ToBytes()
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartObject();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Diagnostics;
Expand Down Expand Up @@ -28,8 +29,8 @@ protected FineTuningCheckpointCollectionPageToken(string jobId, int? pageSize, s

public override BinaryData ToBytes()
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartObject();
writer.WriteString("jobId", JobId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Diagnostics;
Expand Down Expand Up @@ -28,8 +29,8 @@ protected FineTuningEventCollectionPageToken(string jobId, int? limit, string? a

public override BinaryData ToBytes()
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartObject();
writer.WriteString("jobId", JobId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.ClientModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -25,8 +26,8 @@ protected FineTuningEventsPageToken(string jobId, string? after, int? limit)

public override BinaryData ToBytes()
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartObject();

Expand Down
13 changes: 7 additions & 6 deletions OpenAI/src/Custom/Moderations/ModerationClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.TypeSpec.Generator.Customizations;
using System;
using System.Buffers;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
Expand Down Expand Up @@ -238,8 +239,8 @@ public virtual ClientResult<ModerationResult> ClassifyInputs(IEnumerable<Moderat

private void CreateModerationOptions(string input, ref ModerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)


writer.WriteStringValue(input);
writer.Flush();
Expand All @@ -250,8 +251,8 @@ private void CreateModerationOptions(string input, ref ModerationOptions options

private void CreateModerationOptions(IEnumerable<string> inputs, ref ModerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartArray();

Expand All @@ -269,8 +270,8 @@ private void CreateModerationOptions(IEnumerable<string> inputs, ref ModerationO

private void CreateModerationOptions(IEnumerable<ModerationInputPart> inputParts, ref ModerationOptions options)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = OpenAI.MemoryStreamManager.Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

writer.WriteStartArray();
foreach (ModerationInputPart part in inputParts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IO;

namespace OpenAI.Realtime;

Expand All @@ -25,10 +26,10 @@ internal class WebsocketPipelineResponse : PipelineResponse

public override Stream ContentStream
{
get => _contentStream ??= new MemoryStream();
get => _contentStream ??= MemoryStreamManager.Manager.GetStream();
set => throw new NotImplementedException();
}
private MemoryStream _contentStream;
private Microsoft.IO.RecyclableMemoryStream _contentStream;

public override BinaryData Content => _content ??= new(_contentStream.ToArray());
private BinaryData _content;
Comment on lines 27 to 35

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe that Copilot is incorrect here, unfortunately. It looks like the WebsocketPipelineResponse is consumed only by AsyncWebsocketMessageEnumerator which does not currently expose a way to dispose, as it wraps the response in ClientResult<T> when handing back to callers via the enumerator.

ClientResult is not disposable and requires that callers hold responsibility for disposal of messages. While there is a disposal path in the pipeline for HTTP responses, this RealTime implementation appears to lack them.

Expand Down
7 changes: 4 additions & 3 deletions OpenAI/src/Generated/Internal/Utf8JsonBinaryContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#nullable disable

using System.Buffers;
using System.ClientModel;
using System.IO;
using System.Text.Json;
Expand All @@ -12,14 +13,14 @@ namespace OpenAI
{
internal partial class Utf8JsonBinaryContent : BinaryContent
{
private readonly MemoryStream _stream;
private readonly Microsoft.IO.RecyclableMemoryStream _stream;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generated code that cannot be modified by hand. As the generator is an externally owned construct, this is not a path that you'll be able to update.

private readonly BinaryContent _content;

public Utf8JsonBinaryContent()
{
_stream = new MemoryStream();
_stream = MemoryStreamManager.Manager.GetStream();
_content = Create(_stream);
JsonWriter = new Utf8JsonWriter(_stream);
JsonWriter = new Utf8JsonWriter(_stream as IBufferWriter<byte>);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)

}
Comment on lines +16 to 24

public Utf8JsonWriter JsonWriter { get; }
Expand Down
1 change: 1 addition & 0 deletions OpenAI/src/OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
<PackageReference Include="System.ClientModel" />
<PackageReference Include="System.Net.ServerSentEvents" />
</ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions OpenAI/src/Utility/RecyclableMemoryStreamManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.IO;

namespace OpenAI;

internal class MemoryStreamManager

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal class MemoryStreamManager
internal static class MemoryStreamManager

{
public static readonly RecyclableMemoryStreamManager Manager = new ();
}
Comment on lines +5 to +8

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)

Comment on lines +5 to +8
7 changes: 5 additions & 2 deletions examples/Chat/Example08_ChatSerialization.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using NUnit.Framework;
using OpenAI.Chat;
using System;
using System.Buffers;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
Expand All @@ -23,11 +24,13 @@ public static IEnumerable<ChatMessage> DeserializeMessages(BinaryData data)
}
#endregion

private static readonly Microsoft.IO.RecyclableMemoryStreamManager Manager = new ();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll definitely want to preserve use of the standard MemoryStream in examples to reduce complexity.


#region
public static BinaryData SerializeMessages(IEnumerable<ChatMessage> messages)
{
using MemoryStream stream = new();
using Utf8JsonWriter writer = new(stream);
using Microsoft.IO.RecyclableMemoryStream stream = Manager.GetStream();
using Utf8JsonWriter writer = new(stream as IBufferWriter<byte>);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well

https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/blob/master/src/RecyclableMemoryStream.cs#L90

    public sealed class RecyclableMemoryStream : MemoryStream, IBufferWriter<byte>

Tests were passing (until I hit limit)


writer.WriteStartArray();

Expand Down
4 changes: 3 additions & 1 deletion tests/Audio/TranscriptionMockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,13 @@ public async Task TranscribeAudioDeserializesSegment(AudioSourceKind audioSource
Assert.That(segment.NoSpeechProbability, Is.EqualTo(0.2f));
}

private static readonly Microsoft.IO.RecyclableMemoryStreamManager Manager = new ();

[Test]
public void TranscribeAudioFromStreamRespectsTheCancellationToken()
{
AudioClient client = CreateProxyFromClient(new AudioClient("model", s_fakeCredential));
using Stream stream = new MemoryStream();
using Microsoft.IO.RecyclableMemoryStream stream = Manager.GetStream();
using CancellationTokenSource cancellationSource = new();
cancellationSource.Cancel();

Expand Down
4 changes: 3 additions & 1 deletion tests/Audio/TranslationMockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ public async Task TranslateAudioDeserializesSegment(AudioSourceKind audioSourceK
Assert.That(segment.NoSpeechProbability, Is.EqualTo(0.2f));
}

private static readonly Microsoft.IO.RecyclableMemoryStreamManager Manager = new ();

[Test]
public void TranslateAudioFromStreamRespectsTheCancellationToken()
{
AudioClient client = CreateProxyFromClient(new AudioClient("model", s_fakeCredential));
using Stream stream = new MemoryStream();
using Stream stream = Manager.GetStream();
using CancellationTokenSource cancellationSource = new();
cancellationSource.Cancel();

Expand Down
6 changes: 4 additions & 2 deletions tests/Batch/BatchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,12 @@ public void ListBatchesAsync_HonorsCancellationToken()
Assert.ThrowsAsync<TaskCanceledException>(async () => await enumerator.MoveNextAsync().AsTask());
}

private static readonly Microsoft.IO.RecyclableMemoryStreamManager Manager = new ();

[RecordedTest]
public async Task CreateGetAndCancelBatchProtocol()
{
using MemoryStream testFileStream = new();
using Stream testFileStream = Manager.GetStream();
using StreamWriter streamWriter = new(testFileStream);
streamWriter.NewLine = "\r\n";
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?""}]}}";
Expand Down Expand Up @@ -172,7 +174,7 @@ public async Task CreateGetAndCancelBatchProtocol()
[TestCase(false)]
public async Task CanRehydrateBatchOperation(bool useBatchId)
{
using MemoryStream testFileStream = new();
using Stream testFileStream = Manager.GetStream();
using StreamWriter streamWriter = new(testFileStream);
streamWriter.NewLine = "\r\n";
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?""}]}}";
Expand Down
Loading