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,6 +4,7 @@

- Added non-invocable `AIFunctionDeclaration` (base class for `AIFunction`), `AIFunctionFactory.CreateDeclaration`, and `AIFunction.AsDeclarationOnly`.
- Added `[Experimental]` support for user approval of function invocations via `ApprovalRequiredAIFunction`, `FunctionApprovalRequestContent`, and friends.
- Added `[Experimental]` support for MCP server-hosted tools via `HostedMcpServerTool`, `HostedMcpServerToolApprovalMode`, and friends.

## 9.8.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;

#pragma warning disable S109 // Magic numbers should not be used
#pragma warning disable EA0011 // Consider removing unnecessary conditional access operator (?)

namespace Microsoft.Extensions.AI;

/// <summary>
Expand Down Expand Up @@ -42,7 +45,7 @@ public override bool Equals(object? obj) => obj is HostedMcpServerToolRequireSpe

/// <inheritdoc/>
public override int GetHashCode() =>
HashCode.Combine(GetListHashCode(AlwaysRequireApprovalToolNames), GetListHashCode(NeverRequireApprovalToolNames));
Combine(GetListHashCode(AlwaysRequireApprovalToolNames), GetListHashCode(NeverRequireApprovalToolNames));

private static bool ListEquals(IList<string>? list1, IList<string>? list2) =>
ReferenceEquals(list1, list2) ||
Expand All @@ -55,12 +58,32 @@ private static int GetListHashCode(IList<string>? list)
return 0;
}

#if NET
HashCode hc = default;
foreach (string item in list)
for (int i = 0; i < list.Count; i++)
{
hc.Add(item);
hc.Add(list[i]);
}

return hc.ToHashCode();
#else
int hash = 0;
for (int i = 0; i < list.Count; i++)
{
hash = Combine(hash, list[i]?.GetHashCode() ?? 0);
}

return hash;
#endif
}

private static int Combine(int h1, int h2)
{
#if NET
return HashCode.Combine(h1, h2);
#else
uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
return ((int)rol5 + h1) ^ h2;
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<PackageReference Include="System.Text.Json" />
<PackageReference Include="Microsoft.Bcl.HashCode" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## NOT YET RELEASED

- Updated to depend on OpenAI 2.4.0
- Updated tool mappings to recognize any `AIFunctionDeclaration`.
- Updated to accommodate the additions in `Microsoft.Extensions.AI.Abstractions`.
- Updated to depend on OpenAI 2.4.0

## 9.8.0-preview.1.25412.6

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,51 @@ public void Serialization_RequireSpecific_Roundtrips()
HostedMcpServerToolApprovalMode? result = JsonSerializer.Deserialize(json, TestJsonSerializerContext.Default.HostedMcpServerToolApprovalMode);
Assert.Equal(requireSpecific, result);
}

[Fact]
public void Equality_RequireSpecific_WorksAsExpected()
{
var mode1 = HostedMcpServerToolApprovalMode.RequireSpecific(["ToolA", "ToolB"], ["ToolC"]);
var mode2 = HostedMcpServerToolApprovalMode.RequireSpecific(["ToolA", "ToolB"], ["ToolC"]);
Assert.Equal(mode1, mode2);
Assert.Equal(mode1.GetHashCode(), mode2.GetHashCode());

Assert.NotNull(mode1.AlwaysRequireApprovalToolNames);
mode1.AlwaysRequireApprovalToolNames.Add("ToolD");
Assert.NotEqual(mode1, mode2);
Assert.NotEqual(mode1.GetHashCode(), mode2.GetHashCode());

Assert.NotNull(mode2.AlwaysRequireApprovalToolNames);
mode2.AlwaysRequireApprovalToolNames.Add("ToolD");
Assert.Equal(mode1, mode2);
Assert.Equal(mode1.GetHashCode(), mode2.GetHashCode());

Assert.NotNull(mode2.NeverRequireApprovalToolNames);
mode2.NeverRequireApprovalToolNames.Add("ToolE");
Assert.NotEqual(mode1, mode2);
Assert.NotEqual(mode1.GetHashCode(), mode2.GetHashCode());

Assert.NotNull(mode1.NeverRequireApprovalToolNames);
mode1.NeverRequireApprovalToolNames.Add("ToolE");
Assert.Equal(mode1, mode2);
Assert.Equal(mode1.GetHashCode(), mode2.GetHashCode());

var mode3 = HostedMcpServerToolApprovalMode.RequireSpecific(null, null);
Assert.Equal(mode3.GetHashCode(), mode3.GetHashCode());
var mode4 = HostedMcpServerToolApprovalMode.RequireSpecific(["a"], null);
Assert.Equal(mode4.GetHashCode(), mode4.GetHashCode());
Assert.NotEqual(mode3, mode4);
Assert.NotEqual(mode3.GetHashCode(), mode4.GetHashCode());

var mode5 = HostedMcpServerToolApprovalMode.RequireSpecific(null, ["b"]);
Assert.Equal(mode5.GetHashCode(), mode5.GetHashCode());
Assert.NotEqual(mode3, mode5);
Assert.NotEqual(mode3.GetHashCode(), mode5.GetHashCode());
Assert.NotEqual(mode4, mode5);
Assert.NotEqual(mode4.GetHashCode(), mode5.GetHashCode());

var mode6 = HostedMcpServerToolApprovalMode.RequireSpecific([], []);
Assert.Equal(mode6.GetHashCode(), mode6.GetHashCode());
Assert.NotEqual(mode3, mode6);
}
}
Loading