Commit: 80cc152
Parent: eb6fbe0

Review diff with agent

Mårten Åsberg committed on 2026-07-04 at 18:33
In a workflow
Directory.Build.props +8 -1
diff --git a/Directory.Build.props b/Directory.Build.props
index 06ec977..06c4d77 100644
@@ -1,9 +1,16 @@
<Project>
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
<TargetFramework>net11.0-windows10.0.26100</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
<TargetFramework>net11.0</TargetFramework>
<RuntimeIdentifiers>linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
</Project>
Directory.Packages.props +5 -0
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d81292d..e826506 100644
@@ -10,7 +10,12 @@
</GlobalPackageReference>
</ItemGroup>
<ItemGroup>
<PackageVersion Include="ElBruno.MAF.FoundryLocal.Adapter" Version="0.2.0" />
<PackageVersion Include="LibGit2Sharp" Version="0.31.0" />
<PackageVersion Include="Microsoft.Agents.AI" Version="1.12.0" />
<PackageVersion Include="Microsoft.Agents.AI.Workflows" Version="1.12.0" />
<PackageVersion Include="Microsoft.AI.Foundry.Local" Version="1.2.3" />
<PackageVersion Include="Microsoft.AI.Foundry.Local.WinML" Version="1.2.3" />
<PackageVersion
Include="Microsoft.Extensions.FileSystemGlobbing"
Version="11.0.0-preview.5.26302.115"
src/Reviewer.Cli/FoundryLocal/FoundryLocalChatClient.cs +141 -0
diff --git a/src/Reviewer.Cli/FoundryLocal/FoundryLocalChatClient.cs b/src/Reviewer.Cli/FoundryLocal/FoundryLocalChatClient.cs
new file mode 100644
index 0000000..6193f16
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels;
using Microsoft.AI.Foundry.Local;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Reviewer.Cli.FoundryLocal;
internal sealed class FoundryLocalChatClient : IChatClient, IAsyncDisposable
{
private readonly ILogger<FoundryLocalChatClient> logger;
private readonly IServiceProvider serviceProvider;
private readonly CancellationTokenSource initializationCancellationTokenSource;
private readonly Task<IModel> modelTask;
private readonly Task<OpenAIChatClient> chatClientTask;
public FoundryLocalChatClient(
ILogger<FoundryLocalChatClient> logger,
IOptions<FoundryLocalOptions> options,
IServiceProvider serviceProvider
)
{
this.logger = logger;
this.serviceProvider = serviceProvider;
initializationCancellationTokenSource = new();
modelTask = InitializeModel(options.Value, initializationCancellationTokenSource.Token);
chatClientTask = InitializeChatClient(initializationCancellationTokenSource.Token);
}
private async Task<IModel> InitializeModel(FoundryLocalOptions options, CancellationToken cancellationToken)
{
await FoundryLocalManager.CreateAsync(new() { AppName = "ReviewCli" }, logger);
var result = await FoundryLocalManager.Instance.DownloadAndRegisterEpsAsync(cancellationToken);
if (!result.Success)
{
logger.LogUnsuccessfulExecutionProviderRegistration(result.Status);
}
var catalog = await FoundryLocalManager.Instance.GetCatalogAsync(cancellationToken);
var model =
await catalog.GetModelAsync(options.Model, cancellationToken)
?? throw new ModelNotFoundException(options.Model);
if (!await model.IsCachedAsync())
{
await model.DownloadAsync(logger.LogModelDownloadProgress, cancellationToken);
}
await model.LoadAsync(cancellationToken);
return model;
}
private async Task<OpenAIChatClient> InitializeChatClient(CancellationToken cancellationToken)
{
var model = await modelTask;
return await model.GetChatClientAsync(cancellationToken);
}
public async Task<ChatResponse> GetResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
CancellationToken cancellationToken = default
) =>
MapChatResponse(
await (await chatClientTask).CompleteChatAsync(messages.Select(MapChatMessage), cancellationToken)
);
public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
var chatClient = await chatClientTask;
var stream = chatClient.CompleteChatStreamingAsync(messages.Select(MapChatMessage), cancellationToken);
await foreach (var message in stream)
{
yield return MapChatResponseUpdate(message);
}
}
private static Betalgo.Ranul.OpenAI.ObjectModels.RequestModels.ChatMessage MapChatMessage(
ChatMessage chatMessage
) => new(role: chatMessage.Role.Value, content: chatMessage.Text ?? "", name: chatMessage.AuthorName);
private static ChatResponse MapChatResponse(ChatCompletionCreateResponse chatCompletionCreateResponse) =>
new(
new ChatMessage(
ChatRole.Assistant,
chatCompletionCreateResponse
.Choices.Select(c => c?.Message?.ContentCalculated as string ?? c?.Message?.Content)
.OfType<string>()
.First()
)
)
{
RawRepresentation = chatCompletionCreateResponse,
};
private static ChatResponseUpdate MapChatResponseUpdate(
ChatCompletionCreateResponse chatCompletionCreateResponse
) =>
new(
ChatRole.Assistant,
chatCompletionCreateResponse
.Choices.Select(c => c.Message?.ContentCalculated as string ?? c.Message?.Content)
.OfType<string>()
.First()
);
public object? GetService(Type serviceType, object? serviceKey = null) =>
serviceKey is null
? serviceProvider.GetService(serviceType)
: serviceProvider.GetKeyedService(serviceType, serviceKey);
public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
public async ValueTask DisposeAsync()
{
if (modelTask.IsCompletedSuccessfully)
{
var model = await modelTask;
await model.UnloadAsync();
}
else
{
await initializationCancellationTokenSource.CancelAsync();
}
}
}
file sealed class ModelNotFoundException(string modelAlias) : Exception($"Model \"{modelAlias}\" not found.");
src/Reviewer.Cli/FoundryLocal/FoundryLocalLoggerExtensions.cs +12 -0
diff --git a/src/Reviewer.Cli/FoundryLocal/FoundryLocalLoggerExtensions.cs b/src/Reviewer.Cli/FoundryLocal/FoundryLocalLoggerExtensions.cs
new file mode 100644
index 0000000..3406945
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Logging;
namespace Reviewer.Cli.FoundryLocal;
internal static partial class FoundryLocalLoggerExtensions
{
[LoggerMessage(LogLevel.Warning, "Unsuccessfully registered execution providers: {Status}")]
public static partial void LogUnsuccessfulExecutionProviderRegistration(this ILogger logger, string status);
[LoggerMessage(LogLevel.Information, "Downloading model: {Progress}%")]
public static partial void LogModelDownloadProgress(this ILogger logger, float progress);
}
src/Reviewer.Cli/FoundryLocal/FoundryLocalOptions.cs +13 -0
diff --git a/src/Reviewer.Cli/FoundryLocal/FoundryLocalOptions.cs b/src/Reviewer.Cli/FoundryLocal/FoundryLocalOptions.cs
new file mode 100644
index 0000000..d472b22
@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace Reviewer.Cli.FoundryLocal;
internal sealed class FoundryLocalOptions
{
[Required]
public required string Model { get; set; }
}
[OptionsValidator]
internal sealed partial class FoundryLocalOptionsValidator : IValidateOptions<FoundryLocalOptions>;
src/Reviewer.Cli/FoundryLocal/FoundryLocalServiceCollectionExtensions.cs +21 -0
diff --git a/src/Reviewer.Cli/FoundryLocal/FoundryLocalServiceCollectionExtensions.cs b/src/Reviewer.Cli/FoundryLocal/FoundryLocalServiceCollectionExtensions.cs
new file mode 100644
index 0000000..022e7b3
@@ -0,0 +1,21 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
namespace Reviewer.Cli.FoundryLocal;
internal static class FoundryLocalServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddFoundryLocal()
{
services
.AddOptions<FoundryLocalOptions>()
.BindConfiguration("FoundryLocal")
.Validate<FoundryLocalOptionsValidator>()
.ValidateOnStart();
services.AddSingleton<IChatClient, FoundryLocalChatClient>();
return services;
}
}
}
src/Reviewer.Cli/Program.cs +11 -17
diff --git a/src/Reviewer.Cli/Program.cs b/src/Reviewer.Cli/Program.cs
index 1d60db6..10ae2e0 100644
@@ -1,14 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Reviewer;
using Reviewer.Cli;
using Reviewer.Cli.FoundryLocal;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddInputOptions();
builder.Services.AddReviewService();
builder.Services.AddInputOptions().AddFoundryLocal().AddReviewService();
using var app = builder.Build();
@@ -17,17 +19,9 @@ await app.StartAsync();
var reviewService = app.Services.GetRequiredService<ReviewService>();
var inputOptions = app.Services.GetRequiredService<IOptions<InputOptions>>();
reviewService.Review(new(inputOptions.Value.RepositoryPath, inputOptions.Value.CommitHash));
// using System;
// using System.IO;
// using Microsoft.Extensions.FileSystemGlobbing;
await reviewService.Review(
new(inputOptions.Value.RepositoryPath, inputOptions.Value.CommitHash),
CancellationToken.None
);
// var matcher = new Matcher();
// matcher.AddInclude("**");
// matcher.AddExclude("packages.lock.json");
// var results = matcher.GetResultsInFullPath(@"D:\reviewer");
// foreach (var result in results)
// {
// Console.WriteLine(result);
// }
await app.StopAsync();
src/Reviewer.Cli/Reviewer.Cli.csproj +8 -0
diff --git a/src/Reviewer.Cli/Reviewer.Cli.csproj b/src/Reviewer.Cli/Reviewer.Cli.csproj
index 8c18fb8..556bbe2 100644
@@ -16,6 +16,14 @@
<ProjectReference Include="..\Reviewer\Reviewer.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.AI.Foundry.Local"
Condition="!$([MSBuild]::IsOSPlatform('Windows'))"
/>
<PackageReference
Include="Microsoft.AI.Foundry.Local.WinML"
Condition="$([MSBuild]::IsOSPlatform('Windows'))"
/>
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
</Project>
src/Reviewer.Cli/appsettings.Development.json +3 -0
diff --git a/src/Reviewer.Cli/appsettings.Development.json b/src/Reviewer.Cli/appsettings.Development.json
index b94f204..e6cca3a 100644
@@ -1,4 +1,7 @@
{
"FoundryLocal": {
"Model": "qwen2.5-coder-7b"
},
"DiffIgnore": [
"**/packages.lock.json"
]
src/Reviewer.Cli/packages.lock.json +319 -1
diff --git a/src/Reviewer.Cli/packages.lock.json b/src/Reviewer.Cli/packages.lock.json
index d29988f..2ce4e0d 100644
@@ -1,13 +1,24 @@
{
"version": 2,
"dependencies": {
"net11.0": {
"net11.0-windows10.0.26100": {
"CSharpier.MsBuild": {
"type": "Direct",
"requested": "[1.3.0, )",
"resolved": "1.3.0",
"contentHash": "jzzLpmz7F6LyBh5FTZy8/4dEYVcAG9VjBPKryZ82LIoUuupcBkCSXzaeS/4oYcp/rBC8yVLcDstJ8CIv9n2IFw=="
},
"Microsoft.AI.Foundry.Local.WinML": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "WCmMZMURUlT+M3xqTlChHHM8Ol16N2TkNEmT23WTteqCqAOl1FjbLZdpG7OY0cvJ1heAByDoUYWpFhaPqAuErg==",
"dependencies": {
"Betalgo.Ranul.OpenAI": "9.1.0",
"Microsoft.AI.Foundry.Local.Core.WinML": "1.2.3",
"Microsoft.Extensions.Logging": "9.0.15"
}
},
"Microsoft.Extensions.Hosting": {
"type": "Direct",
"requested": "[11.0.0-preview.5.26302.115, )",
@@ -32,11 +43,80 @@
"Microsoft.Extensions.Logging.EventSource": "11.0.0-preview.5.26302.115"
}
},
"Betalgo.Ranul.OpenAI": {
"type": "Transitive",
"resolved": "9.1.0",
"contentHash": "PwGW3nvCI65LQPu3EFdZfpB3Z9LNM3NTMGL6ewIrPXLxxCuMvwwJI87BAFQCvZhjILt49Me+gFu0HWQWYjtycA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.0",
"Microsoft.Extensions.AI.Abstractions": "9.5.0",
"Microsoft.Extensions.Http": "9.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.0"
}
},
"Google.Protobuf": {
"type": "Transitive",
"resolved": "3.30.2",
"contentHash": "Y2aOVLIt75yeeEWigg9V9YnjsEm53sADtLGq0gLhwaXpk3iu8tYSoauolyhenagA2sWno2TQ2WujI0HQd6s1Vw=="
},
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
},
"Microsoft.Agents.AI.Abstractions": {
"type": "Transitive",
"resolved": "1.12.0",
"contentHash": "9eDJnseXiM3j38jrdpkFi2cxWZG80fuydtQ0I9dcUksi+FilMenu5vMCCLmEunIWLka1bd6s7ARbB9MXB4+8pw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0"
}
},
"Microsoft.AI.Foundry.Local.Core.WinML": {
"type": "Transitive",
"resolved": "1.2.3",
"contentHash": "WI7rV33dR7MybYnN/8qU12qmD/+yhFYoTnpDHTwbzoxtscJEOR+zeNzQCUHAKt5ETaRGnSi7MhQfGHti1x9aaQ==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.26.0",
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": "0.14.1",
"Microsoft.Windows.AI.MachineLearning": "2.1.1"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "9.0.0",
"contentHash": "owmu2Cr3IQ8yQiBleBHlGk8dSQ12oaF2e7TpzwJKEl4m84kkZJjEY1n33L67Y3zM5jPOjmmbdHjbfiL0RqcMRQ=="
},
"Microsoft.Extensions.AI": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "30EQA5M7ouoC1rhK0WARh+qm0CSvHToEV9MwkFY7tp42RAFnofirklXP0R/E9xi3pcbJtkNs0Ty2wromtUgXLw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"System.Numerics.Tensors": "10.0.8"
}
},
"Microsoft.Extensions.AI.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "JCkVoyqBqi9r3Vs5Qo5PfZobT0+CWeOx+9y9yggBxtW9s/X5CAoa7Iw/tP/rfUdz+nxRKMSBmrytYgwmu60lgA=="
},
"Microsoft.Extensions.AI.Evaluation": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "fHu9H93p7jM2KiXC/6Kd511Gly1rR/VVSIr0FpQ0K381KSHNkSKgp7UAceAPe8wuUMBNXlhcI0q8UocjneuBVg==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0"
}
},
"Microsoft.Extensions.Compliance.Abstractions": {
"type": "Transitive",
"resolved": "10.5.0",
"contentHash": "xbWZji13Vb2jDJNtwVrKpI09jd8x3n3fL+GzhiLK+8O5Wc2A+GyqCZalST2fV46Pf0QfCwkXf83y+3/rDkCd7A==",
"dependencies": {
"Microsoft.Extensions.ObjectPool": "10.0.6"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
@@ -115,6 +195,15 @@
"Microsoft.Extensions.FileSystemGlobbing": "11.0.0-preview.5.26302.115"
}
},
"Microsoft.Extensions.Http": {
"type": "Transitive",
"resolved": "9.0.0",
"contentHash": "DqI4q54U4hH7bIAq9M5a/hl5Odr/KBAoaZ0dcT4OgutD8dook34CbkvAfAIzkMVjYXiL+E5ul9etwwqiX4PHGw==",
"dependencies": {
"Microsoft.Extensions.Diagnostics": "9.0.0",
"Microsoft.Extensions.Logging": "9.0.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
@@ -169,15 +258,98 @@
"Microsoft.Extensions.Logging": "11.0.0-preview.5.26302.115"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "2Jafd4fdxxiwiQ08mcF+Lf3vqikkQZusGVThOKZNSmPDceGk4IwkjeHL7OEb9Ov8q9ICY5wofL98CS153K5VvQ=="
},
"Microsoft.Extensions.VectorData.Abstractions": {
"type": "Transitive",
"resolved": "9.7.0",
"contentHash": "Vth/omSCX2vR0JabzSRU/hdPhr0CvUVZlaS2lJPWHrEwvak8ntrQLDtLMtMiWKSvviGBe/WmjUW8gA3qqn9tjw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "9.5.0"
}
},
"Microsoft.ML.OnnxRuntime.Foundry": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "R7ojS2ixajwN6p4vSL2cABwvVU3A5nqxwuT2KzK5iMCRnn1VeqSi8qGqe5iBShAUHyZV+ZS8XQY6FLcyEuPvfA==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Gpu.Linux": "1.26.0",
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntime.Gpu.Linux": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "TuVEsaYaiLyy5e18TFRZxyXAs7/Hxvb4LLoj13IWp2m/xPno5gSOZc+B+DD6/Pi7pMdZSgG5MmLR1IOFH4b7gg==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntime.Managed": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "hEmwYh5WL8n1xjNvmfdSUOqGGXAKCLtxfgUfGZRRAen9IWuSk7ieoBFH08+wlrYp1Vma80HtAirL3QLDbTeEzQ==",
"dependencies": {
"System.Numerics.Tensors": "9.0.0"
}
},
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": {
"type": "Transitive",
"resolved": "0.14.1",
"contentHash": "rKIOaAyF9Q/6Wl83PbdAaSckSNcdrpbnZmzNYNMzgVLoXwcJJebtRCt72rqBxVXBeYl4irC1WIG+nBDffc04aw==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.23.2",
"Microsoft.ML.OnnxRuntimeGenAI.Managed": "0.14.1"
}
},
"Microsoft.ML.OnnxRuntimeGenAI.Managed": {
"type": "Transitive",
"resolved": "0.14.1",
"contentHash": "I5OCZdyAil9U9lDAYiDMtLF5teoGgU4tIIv3I3+iA3C/2iTLGiSxFTZkaYJ97RV1DgtqWNpTAo/LlvzbcfZ7mA==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "9.8.0"
}
},
"Microsoft.ML.Tokenizers": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "+b8lT4cLLO/sBR2hjvE/qG6qrZG15h7/PBvnIrzTh4xDaAxdHUY6449rC+1pHzQUsBiCHZVbj+VMn+xS0sL7TA==",
"dependencies": {
"Google.Protobuf": "3.30.2"
}
},
"Microsoft.Windows.AI.MachineLearning": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "A5BbYPLVrcaRJvpwx0nv7aMDOMqR20qaw9EaLcUe9pkSArzKoSk6cilRsSioU+eRa9+HahidBZTegawdswKqIQ==",
"dependencies": {
"System.Numerics.Tensors": "9.0.0"
}
},
"OpenTelemetry.Api": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "fX+fkCysfPut+qCcT3bKqyX4QN9Saf4CgX8HLOHywEVD+Xr7sULtfuypITpoDysjx8R59dn/3mWhgimMH8cm/g=="
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
"contentHash": "rbMn13FzntTkgxfdyDtfXwiwV1U6Biyts64lg93MvNjCM+JsmzC+e2l+KD8Mldcey6vXocjcBuopL9rT1X2MRg=="
},
"System.Numerics.Tensors": {
"type": "Transitive",
"resolved": "10.0.8",
"contentHash": "pcyglC5R0BXGyD8cntjFXJhACpPyk7H7eN0zKW/5d5MIcmRUPVZrjytZ2Bsn9igzQJGg5xIOOokPHFLnfiEuxQ=="
},
"reviewer": {
"type": "Project",
"dependencies": {
"LibGit2Sharp": "[0.31.0, )",
"Microsoft.Agents.AI": "[1.12.0, )",
"Microsoft.Agents.AI.Workflows": "[1.12.0, )",
"Microsoft.Extensions.FileSystemGlobbing": "[11.0.0-preview.5.26302.115, )",
"Microsoft.Extensions.Options.ConfigurationExtensions": "[11.0.0-preview.5.26302.115, )"
}
@@ -191,6 +363,40 @@
"LibGit2Sharp.NativeBinaries": "[2.0.323]"
}
},
"Microsoft.Agents.AI": {
"type": "CentralTransitive",
"requested": "[1.12.0, )",
"resolved": "1.12.0",
"contentHash": "a7r3cu6nO+hhZI3NBRNQIpOWxwEHWpnl0wdpCrO0RtJ8ucEcMLbXsjPfrdCprpladt9IhCDoRpOCKS4ShjICCg==",
"dependencies": {
"Microsoft.Agents.AI.Abstractions": "1.12.0",
"Microsoft.Extensions.AI": "10.6.0",
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"Microsoft.Extensions.AI.Evaluation": "10.6.0",
"Microsoft.Extensions.Compliance.Abstractions": "10.5.0",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.6",
"Microsoft.Extensions.VectorData.Abstractions": "9.7.0",
"Microsoft.ML.Tokenizers": "2.0.0"
}
},
"Microsoft.Agents.AI.Workflows": {
"type": "CentralTransitive",
"requested": "[1.12.0, )",
"resolved": "1.12.0",
"contentHash": "Av57La/JprgVue86N7luXHXwNxw6mZ/0QBUqkJxY7LCnns4VcS2Fas0mdTJVS3v11UmboHg1wzH7VRSftxAsqg==",
"dependencies": {
"Microsoft.Agents.AI": "1.12.0",
"Microsoft.Agents.AI.Abstractions": "1.12.0",
"Microsoft.Extensions.AI": "10.6.0",
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"Microsoft.Extensions.AI.Evaluation": "10.6.0",
"Microsoft.Extensions.Compliance.Abstractions": "10.5.0",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.6",
"Microsoft.Extensions.VectorData.Abstractions": "9.7.0",
"Microsoft.ML.Tokenizers": "2.0.0",
"OpenTelemetry.Api": "1.15.3"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "CentralTransitive",
"requested": "[11.0.0-preview.5.26302.115, )",
@@ -206,6 +412,118 @@
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.5.26302.115"
}
}
},
"net11.0-windows10.0.26100/win-arm64": {
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
},
"Microsoft.AI.Foundry.Local.Core.WinML": {
"type": "Transitive",
"resolved": "1.2.3",
"contentHash": "WI7rV33dR7MybYnN/8qU12qmD/+yhFYoTnpDHTwbzoxtscJEOR+zeNzQCUHAKt5ETaRGnSi7MhQfGHti1x9aaQ==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.26.0",
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": "0.14.1",
"Microsoft.Windows.AI.MachineLearning": "2.1.1"
}
},
"Microsoft.ML.OnnxRuntime.Foundry": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "R7ojS2ixajwN6p4vSL2cABwvVU3A5nqxwuT2KzK5iMCRnn1VeqSi8qGqe5iBShAUHyZV+ZS8XQY6FLcyEuPvfA==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Gpu.Linux": "1.26.0",
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntime.Gpu.Linux": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "TuVEsaYaiLyy5e18TFRZxyXAs7/Hxvb4LLoj13IWp2m/xPno5gSOZc+B+DD6/Pi7pMdZSgG5MmLR1IOFH4b7gg==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": {
"type": "Transitive",
"resolved": "0.14.1",
"contentHash": "rKIOaAyF9Q/6Wl83PbdAaSckSNcdrpbnZmzNYNMzgVLoXwcJJebtRCt72rqBxVXBeYl4irC1WIG+nBDffc04aw==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.23.2",
"Microsoft.ML.OnnxRuntimeGenAI.Managed": "0.14.1"
}
},
"Microsoft.Windows.AI.MachineLearning": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "A5BbYPLVrcaRJvpwx0nv7aMDOMqR20qaw9EaLcUe9pkSArzKoSk6cilRsSioU+eRa9+HahidBZTegawdswKqIQ==",
"dependencies": {
"System.Numerics.Tensors": "9.0.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
"contentHash": "rbMn13FzntTkgxfdyDtfXwiwV1U6Biyts64lg93MvNjCM+JsmzC+e2l+KD8Mldcey6vXocjcBuopL9rT1X2MRg=="
}
},
"net11.0-windows10.0.26100/win-x64": {
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
},
"Microsoft.AI.Foundry.Local.Core.WinML": {
"type": "Transitive",
"resolved": "1.2.3",
"contentHash": "WI7rV33dR7MybYnN/8qU12qmD/+yhFYoTnpDHTwbzoxtscJEOR+zeNzQCUHAKt5ETaRGnSi7MhQfGHti1x9aaQ==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.26.0",
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": "0.14.1",
"Microsoft.Windows.AI.MachineLearning": "2.1.1"
}
},
"Microsoft.ML.OnnxRuntime.Foundry": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "R7ojS2ixajwN6p4vSL2cABwvVU3A5nqxwuT2KzK5iMCRnn1VeqSi8qGqe5iBShAUHyZV+ZS8XQY6FLcyEuPvfA==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Gpu.Linux": "1.26.0",
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntime.Gpu.Linux": {
"type": "Transitive",
"resolved": "1.26.0",
"contentHash": "TuVEsaYaiLyy5e18TFRZxyXAs7/Hxvb4LLoj13IWp2m/xPno5gSOZc+B+DD6/Pi7pMdZSgG5MmLR1IOFH4b7gg==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Managed": "1.26.0"
}
},
"Microsoft.ML.OnnxRuntimeGenAI.Foundry": {
"type": "Transitive",
"resolved": "0.14.1",
"contentHash": "rKIOaAyF9Q/6Wl83PbdAaSckSNcdrpbnZmzNYNMzgVLoXwcJJebtRCt72rqBxVXBeYl4irC1WIG+nBDffc04aw==",
"dependencies": {
"Microsoft.ML.OnnxRuntime.Foundry": "1.23.2",
"Microsoft.ML.OnnxRuntimeGenAI.Managed": "0.14.1"
}
},
"Microsoft.Windows.AI.MachineLearning": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "A5BbYPLVrcaRJvpwx0nv7aMDOMqR20qaw9EaLcUe9pkSArzKoSk6cilRsSioU+eRa9+HahidBZTegawdswKqIQ==",
"dependencies": {
"System.Numerics.Tensors": "9.0.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
"contentHash": "rbMn13FzntTkgxfdyDtfXwiwV1U6Biyts64lg93MvNjCM+JsmzC+e2l+KD8Mldcey6vXocjcBuopL9rT1X2MRg=="
}
}
}
}
\ No newline at end of file
src/Reviewer/Agents/OrchestratorAgent.cs +29 -0
diff --git a/src/Reviewer/Agents/OrchestratorAgent.cs b/src/Reviewer/Agents/OrchestratorAgent.cs
new file mode 100644
index 0000000..42b5690
@@ -0,0 +1,29 @@
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Reviewer.Agents;
internal static class OrchestratorAgent
{
extension(IServiceCollection services)
{
public IServiceCollection AddOrchestratorAgent() =>
services.AddKeyedTransient<AIAgent>(
nameof(OrchestratorAgent),
(sp, key) =>
{
var chatClient = sp.GetRequiredService<IChatClient>();
return chatClient.AsAIAgent(
name: nameof(OrchestratorAgent),
instructions: """
You are a code reviewer. You will be presented with a diff and your task is to review the changes.
""",
loggerFactory: sp.GetRequiredService<ILoggerFactory>(),
services: sp
);
}
);
}
}
src/Reviewer/ReviewService.cs +26 -33
diff --git a/src/Reviewer/ReviewService.cs b/src/Reviewer/ReviewService.cs
index 936fcc7..b757290 100644
@@ -1,47 +1,40 @@
using LibGit2Sharp;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Reviewer.Workflow;
namespace Reviewer;
public sealed class ReviewService(ILogger<ReviewService> logger, DiffProcessor diffProcessor)
public sealed class ReviewService(
ILogger<ReviewService> logger,
[FromKeyedServices("ReviewWorkflow")] Microsoft.Agents.AI.Workflows.Workflow workflow
)
{
public void Review(Input input)
public async Task<string?> Review(Input input, CancellationToken cancellationToken)
{
using var repository = OpenRepository(input.RepositoryPath);
if (repository is null)
{
logger.LogNoRepositoryFound(input.RepositoryPath);
return;
}
var commit = repository.Lookup<Commit>(input.CommitHash);
if (commit is null)
{
logger.LogNoCommitFound(input.CommitHash);
return;
}
var diff = diffProcessor.ProduceDiff(commit);
logger.LogInformation(
"Reviewing commit \"{Message}\" ({Hash}) by {Author} with {ChangeCount} patches",
commit.MessageShort,
commit.Id,
commit.Author.Name,
diff.Patches.Length
await using var run = await InProcessExecution.RunStreamingAsync(
workflow,
input,
cancellationToken: cancellationToken
);
logger.LogInformation("Patch:\n{Patch}", diff.ToString());
}
private static Repository? OpenRepository(string path)
{
try
var events = new List<WorkflowEvent>();
await foreach (var e in run.WatchStreamAsync(cancellationToken))
{
return new Repository(path);
events.Add(e);
}
catch (RepositoryNotFoundException)
logger.LogInformation("Workflow completed with {EventCount} events", events);
var response = events.OfType<AgentResponseEvent>().LastOrDefault()?.Response;
if (response is not null)
{
return null;
logger.LogInformation("Review: {Review}", response.Text);
}
return response?.Text;
}
}
@@ -49,6 +42,6 @@ public static class ReviewServiceServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddReviewService() => services.AddTransient<ReviewService>().AddDiffProcessor();
public IServiceCollection AddReviewService() => services.AddTransient<ReviewService>().AddReviewWorkflow();
}
}
src/Reviewer/ReviewableDiff.cs +1 -1
diff --git a/src/Reviewer/ReviewableDiff.cs b/src/Reviewer/ReviewableDiff.cs
index 1a6783a..1e7f360 100644
@@ -3,7 +3,7 @@ using LibGit2Sharp;
namespace Reviewer;
internal sealed record ReviewableDiff(PatchEntryChanges[] Patches)
public sealed record ReviewableDiff(PatchEntryChanges[] Patches)
{
public override string ToString() => string.Concat(Patches.Select(p => p.Patch));
}
src/Reviewer/Reviewer.csproj +2 -0
diff --git a/src/Reviewer/Reviewer.csproj b/src/Reviewer/Reviewer.csproj
index 4a1bc4a..b645347 100644
@@ -5,6 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LibGit2Sharp" />
<PackageReference Include="Microsoft.Agents.AI" />
<PackageReference Include="Microsoft.Agents.AI.Workflows" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>
src/Reviewer/Workflow/DiffProcessor.cs +11 -3
diff --git a/src/Reviewer/DiffProcessor.cs b/src/Reviewer/Workflow/DiffProcessor.cs
similarity index 87%
rename from src/Reviewer/DiffProcessor.cs
rename to src/Reviewer/Workflow/DiffProcessor.cs
index bdba161..bf39f95 100644
@@ -1,18 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using LibGit2Sharp;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Options;
namespace Reviewer;
namespace Reviewer.Workflow;
public sealed class DiffProcessor(IOptions<DiffIgnoreOptions> options)
: Executor<Commit, ReviewableDiff>(nameof(DiffProcessor))
{
private readonly Matcher matcher = Matcher.CreateExclude(options.Value.DiffIgnore);
internal ReviewableDiff ProduceDiff(Commit commit)
public override ValueTask<ReviewableDiff> HandleAsync(
Commit commit,
IWorkflowContext context,
CancellationToken cancellationToken = default
)
{
var repository = ((IBelongToARepository)commit).Repository;
var parent = GetParentCommit(commit);
@@ -20,7 +28,7 @@ public sealed class DiffProcessor(IOptions<DiffIgnoreOptions> options)
var filteredPatches = patches.Where(e => matcher.IsMatch(e.Path));
return new([.. filteredPatches]);
return ValueTask.FromResult(new ReviewableDiff([.. filteredPatches]));
}
private static Commit? GetParentCommit(Commit commit) =>
src/Reviewer/Workflow/InputProcessor.cs +32 -0
diff --git a/src/Reviewer/Workflow/InputProcessor.cs b/src/Reviewer/Workflow/InputProcessor.cs
new file mode 100644
index 0000000..f996709
@@ -0,0 +1,32 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using LibGit2Sharp;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.DependencyInjection;
namespace Reviewer.Workflow;
internal sealed class InputProcessor() : Executor<Input, Commit>(nameof(InputProcessor))
{
public override ValueTask<Commit> HandleAsync(
Input input,
IWorkflowContext context,
CancellationToken cancellationToken = default
)
{
var repository = new Repository(input.RepositoryPath);
var commit = repository.Lookup<Commit>(input.CommitHash) ?? throw new CommitNotFoundException(input.CommitHash);
return ValueTask.FromResult(commit);
}
}
file sealed class CommitNotFoundException(string commitHash) : Exception($"No commit found for \"{commitHash}\".");
internal static class InputProcessorServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddInputProcessor() => services.AddTransient<InputProcessor>();
}
}
src/Reviewer/Workflow/OrchestratorPromptBuilder.cs +32 -0
diff --git a/src/Reviewer/Workflow/OrchestratorPromptBuilder.cs b/src/Reviewer/Workflow/OrchestratorPromptBuilder.cs
new file mode 100644
index 0000000..50b3277
@@ -0,0 +1,32 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
namespace Reviewer.Workflow;
[SendsMessage(typeof(ChatMessage))]
[SendsMessage(typeof(TurnToken))]
internal sealed class OrchestratorPromptBuilder() : Executor<ReviewableDiff>(nameof(OrchestratorPromptBuilder))
{
public override async ValueTask HandleAsync(
ReviewableDiff reviewableDiff,
IWorkflowContext context,
CancellationToken cancellationToken = default
)
{
var chatMessage = new ChatMessage(ChatRole.User, reviewableDiff.ToString());
await context.SendMessageAsync(chatMessage, cancellationToken);
await context.SendMessageAsync(new TurnToken(emitEvents: true), cancellationToken);
}
}
internal static class OrchestratorPromptBuilderServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddOrchestratorPromptBuilder() => services.AddTransient<OrchestratorPromptBuilder>();
}
}
src/Reviewer/Workflow/WorkflowServiceCollectionExtensions.cs +41 -0
diff --git a/src/Reviewer/Workflow/WorkflowServiceCollectionExtensions.cs b/src/Reviewer/Workflow/WorkflowServiceCollectionExtensions.cs
new file mode 100644
index 0000000..a4e2016
@@ -0,0 +1,41 @@
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Reviewer.Agents;
namespace Reviewer.Workflow;
internal static class WorkflowServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddReviewWorkflow() =>
services
.AddInputProcessor()
.AddDiffProcessor()
.AddOrchestratorPromptBuilder()
.AddOrchestratorAgent()
.AddKeyedTransient(
"ReviewWorkflow",
(sp, key) =>
{
var inputProcessor = sp.GetRequiredService<InputProcessor>();
var diffProcessor = sp.GetRequiredService<DiffProcessor>();
var orchestratorPromptBuilder = sp.GetRequiredService<OrchestratorPromptBuilder>();
var hostOptions = new AIAgentHostOptions
{
EmitAgentResponseEvents = true,
ForwardIncomingMessages = false,
};
var orchestratorAgent = sp.GetRequiredKeyedService<AIAgent>(nameof(OrchestratorAgent))
.BindAsExecutor(hostOptions);
return new WorkflowBuilder(inputProcessor)
.AddEdge(inputProcessor, diffProcessor)
.AddEdge(diffProcessor, orchestratorPromptBuilder)
.AddEdge(orchestratorPromptBuilder, orchestratorAgent)
.WithOutputFrom(orchestratorAgent)
.Build();
}
);
}
}
src/Reviewer/packages.lock.json +123 -1
diff --git a/src/Reviewer/packages.lock.json b/src/Reviewer/packages.lock.json
index 352ce63..711fa56 100644
@@ -1,7 +1,7 @@
{
"version": 2,
"dependencies": {
"net11.0": {
"net11.0-windows10.0.26100": {
"CSharpier.MsBuild": {
"type": "Direct",
"requested": "[1.3.0, )",
@@ -17,6 +17,40 @@
"LibGit2Sharp.NativeBinaries": "[2.0.323]"
}
},
"Microsoft.Agents.AI": {
"type": "Direct",
"requested": "[1.12.0, )",
"resolved": "1.12.0",
"contentHash": "a7r3cu6nO+hhZI3NBRNQIpOWxwEHWpnl0wdpCrO0RtJ8ucEcMLbXsjPfrdCprpladt9IhCDoRpOCKS4ShjICCg==",
"dependencies": {
"Microsoft.Agents.AI.Abstractions": "1.12.0",
"Microsoft.Extensions.AI": "10.6.0",
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"Microsoft.Extensions.AI.Evaluation": "10.6.0",
"Microsoft.Extensions.Compliance.Abstractions": "10.5.0",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.6",
"Microsoft.Extensions.VectorData.Abstractions": "9.7.0",
"Microsoft.ML.Tokenizers": "2.0.0"
}
},
"Microsoft.Agents.AI.Workflows": {
"type": "Direct",
"requested": "[1.12.0, )",
"resolved": "1.12.0",
"contentHash": "Av57La/JprgVue86N7luXHXwNxw6mZ/0QBUqkJxY7LCnns4VcS2Fas0mdTJVS3v11UmboHg1wzH7VRSftxAsqg==",
"dependencies": {
"Microsoft.Agents.AI": "1.12.0",
"Microsoft.Agents.AI.Abstractions": "1.12.0",
"Microsoft.Extensions.AI": "10.6.0",
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"Microsoft.Extensions.AI.Evaluation": "10.6.0",
"Microsoft.Extensions.Compliance.Abstractions": "10.5.0",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.6",
"Microsoft.Extensions.VectorData.Abstractions": "9.7.0",
"Microsoft.ML.Tokenizers": "2.0.0",
"OpenTelemetry.Api": "1.15.3"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Direct",
"requested": "[11.0.0-preview.5.26302.115, )",
@@ -32,11 +66,54 @@
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.5.26302.115"
}
},
"Google.Protobuf": {
"type": "Transitive",
"resolved": "3.30.2",
"contentHash": "Y2aOVLIt75yeeEWigg9V9YnjsEm53sADtLGq0gLhwaXpk3iu8tYSoauolyhenagA2sWno2TQ2WujI0HQd6s1Vw=="
},
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
},
"Microsoft.Agents.AI.Abstractions": {
"type": "Transitive",
"resolved": "1.12.0",
"contentHash": "9eDJnseXiM3j38jrdpkFi2cxWZG80fuydtQ0I9dcUksi+FilMenu5vMCCLmEunIWLka1bd6s7ARbB9MXB4+8pw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0"
}
},
"Microsoft.Extensions.AI": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "30EQA5M7ouoC1rhK0WARh+qm0CSvHToEV9MwkFY7tp42RAFnofirklXP0R/E9xi3pcbJtkNs0Ty2wromtUgXLw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0",
"System.Numerics.Tensors": "10.0.8"
}
},
"Microsoft.Extensions.AI.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "JCkVoyqBqi9r3Vs5Qo5PfZobT0+CWeOx+9y9yggBxtW9s/X5CAoa7Iw/tP/rfUdz+nxRKMSBmrytYgwmu60lgA=="
},
"Microsoft.Extensions.AI.Evaluation": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "fHu9H93p7jM2KiXC/6Kd511Gly1rR/VVSIr0FpQ0K381KSHNkSKgp7UAceAPe8wuUMBNXlhcI0q8UocjneuBVg==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.6.0"
}
},
"Microsoft.Extensions.Compliance.Abstractions": {
"type": "Transitive",
"resolved": "10.5.0",
"contentHash": "xbWZji13Vb2jDJNtwVrKpI09jd8x3n3fL+GzhiLK+8O5Wc2A+GyqCZalST2fV46Pf0QfCwkXf83y+3/rDkCd7A==",
"dependencies": {
"Microsoft.Extensions.ObjectPool": "10.0.6"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "11.0.0-preview.5.26302.115",
@@ -49,6 +126,51 @@
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.5.26302.115"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "2Jafd4fdxxiwiQ08mcF+Lf3vqikkQZusGVThOKZNSmPDceGk4IwkjeHL7OEb9Ov8q9ICY5wofL98CS153K5VvQ=="
},
"Microsoft.Extensions.VectorData.Abstractions": {
"type": "Transitive",
"resolved": "9.7.0",
"contentHash": "Vth/omSCX2vR0JabzSRU/hdPhr0CvUVZlaS2lJPWHrEwvak8ntrQLDtLMtMiWKSvviGBe/WmjUW8gA3qqn9tjw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "9.5.0"
}
},
"Microsoft.ML.Tokenizers": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "+b8lT4cLLO/sBR2hjvE/qG6qrZG15h7/PBvnIrzTh4xDaAxdHUY6449rC+1pHzQUsBiCHZVbj+VMn+xS0sL7TA==",
"dependencies": {
"Google.Protobuf": "3.30.2"
}
},
"OpenTelemetry.Api": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "fX+fkCysfPut+qCcT3bKqyX4QN9Saf4CgX8HLOHywEVD+Xr7sULtfuypITpoDysjx8R59dn/3mWhgimMH8cm/g=="
},
"System.Numerics.Tensors": {
"type": "Transitive",
"resolved": "10.0.8",
"contentHash": "pcyglC5R0BXGyD8cntjFXJhACpPyk7H7eN0zKW/5d5MIcmRUPVZrjytZ2Bsn9igzQJGg5xIOOokPHFLnfiEuxQ=="
}
},
"net11.0-windows10.0.26100/win-arm64": {
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
}
},
"net11.0-windows10.0.26100/win-x64": {
"LibGit2Sharp.NativeBinaries": {
"type": "Transitive",
"resolved": "2.0.323",
"contentHash": "Kg+fJGWhGj5qRXG0Ilj4ddhuodGXZg57yhfX6OVUDR0M2DKg/UR42/d74+qv5l1qotc1qJilo/ho7xQnULP6yA=="
}
}
}