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
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
using System;
using System.Text.Json.Nodes;

#pragma warning disable S1067 // Expressions should not be too complex

namespace Microsoft.Extensions.AI;

/// <summary>
/// Provides options for configuring the behavior of <see cref="AIJsonUtilities"/> JSON schema creation functionality.
/// </summary>
public sealed class AIJsonSchemaCreateOptions
public sealed class AIJsonSchemaCreateOptions : IEquatable<AIJsonSchemaCreateOptions>
{
/// <summary>
/// Gets the default options instance.
Expand Down Expand Up @@ -40,4 +42,21 @@ public sealed class AIJsonSchemaCreateOptions
/// Gets a value indicating whether to mark all properties as required in the schema.
/// </summary>
public bool RequireAllProperties { get; init; } = true;

/// <inheritdoc/>
public bool Equals(AIJsonSchemaCreateOptions? other)
{
return other is not null &&
TransformSchemaNode == other.TransformSchemaNode &&
IncludeTypeInEnumSchemas == other.IncludeTypeInEnumSchemas &&
DisallowAdditionalProperties == other.DisallowAdditionalProperties &&
IncludeSchemaKeyword == other.IncludeSchemaKeyword &&
RequireAllProperties == other.RequireAllProperties;
}

/// <inheritdoc />
public override bool Equals(object? obj) => obj is AIJsonSchemaCreateOptions other && Equals(other);

/// <inheritdoc />
public override int GetHashCode() => (TransformSchemaNode, IncludeTypeInEnumSchemas, DisallowAdditionalProperties, IncludeSchemaKeyword, RequireAllProperties).GetHashCode();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Shared.Diagnostics;

Expand Down Expand Up @@ -30,4 +34,104 @@ internal static string SanitizeMemberName(string memberName)
private static Regex InvalidNameCharsRegex() => _invalidNameCharsRegex;
private static readonly Regex _invalidNameCharsRegex = new("[^0-9A-Za-z_]", RegexOptions.Compiled);
#endif

/// <summary>Invokes the MethodInfo with the specified target object and arguments.</summary>
private static object? ReflectionInvoke(MethodInfo method, object? target, object?[]? arguments)
{
#if NET
return method.Invoke(target, BindingFlags.DoNotWrapExceptions, binder: null, arguments, culture: null);
#else
try
{
return method.Invoke(target, BindingFlags.Default, binder: null, arguments, culture: null);
}
catch (TargetInvocationException e) when (e.InnerException is not null)
{
// If we're targeting .NET Framework, such that BindingFlags.DoNotWrapExceptions
// is ignored, the original exception will be wrapped in a TargetInvocationException.
// Unwrap it and throw that original exception, maintaining its stack information.
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(e.InnerException).Throw();
throw;
}
#endif
}

/// <summary>
/// Implements a simple write-only memory stream that uses pooled buffers.
/// </summary>
private sealed class PooledMemoryStream : Stream
{
private const int DefaultBufferSize = 4096;
private byte[] _buffer;
private int _position;

public PooledMemoryStream(int initialCapacity = DefaultBufferSize)
{
_buffer = ArrayPool<byte>.Shared.Rent(initialCapacity);
_position = 0;
}

public ReadOnlySpan<byte> GetBuffer() => _buffer.AsSpan(0, _position);
public override bool CanWrite => true;
public override bool CanRead => false;
public override bool CanSeek => false;
public override long Length => _position;
public override long Position
{
get => _position;
set => throw new NotSupportedException();
}

public override void Write(byte[] buffer, int offset, int count)
{
EnsureNotDisposed();
EnsureCapacity(_position + count);

Buffer.BlockCopy(buffer, offset, _buffer, _position, count);
_position += count;
}

public override void Flush()
{
}

public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();

protected override void Dispose(bool disposing)
{
if (_buffer is not null)
{
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = null!;
}

base.Dispose(disposing);
}

private void EnsureCapacity(int requiredCapacity)
{
if (requiredCapacity <= _buffer.Length)
{
return;
}

int newCapacity = Math.Max(requiredCapacity, _buffer.Length * 2);
byte[] newBuffer = ArrayPool<byte>.Shared.Rent(newCapacity);
Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _position);

ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
}

private void EnsureNotDisposed()
{
if (_buffer is null)
{
Throw();
static void Throw() => throw new ObjectDisposedException(nameof(PooledMemoryStream));
}
}
}
}
Loading