Update PipelineAgent/ChangesChecker/ChangesCheckerAgent.cs

This commit is contained in:
2026-01-25 11:36:17 +00:00
parent 40e6c00543
commit 0a5b383329

View File

@@ -6,17 +6,13 @@ using PipelineAgent.ChangesChecker.Models;
using Services.Gitea;
using Services.OpenAI;
using Services.Vault;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace PipelineAgent.ChangesChecker;
public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vaultService, IGiteaService giteaService, ILogger<ChangesCheckerAgent> logger)
public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vaultService, IGiteaService giteaService)
: IChangesCheckerAgent
{
private readonly ILogger<ChangesCheckerAgent> _logger = logger;
public async Task<int> CheckChangesAsync(string repositoryPath, CancellationToken cancellationToken = default)
public async Task<int> CheckChangesAsync(string repositoryPath)
{
try
{
@@ -24,29 +20,30 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
.ConfigureAwait(false);
var giteaConfiguration = GetGiteaConfiguration(repositoryPath, giteaApiToken);
var giteaClient = giteaService.CreateGiteaClient(giteaConfiguration);
var lastChangesFromGitea = await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration, cancellationToken).ConfigureAwait(false);
var lastChangesFromGitea =
await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration).ConfigureAwait(false);
if (lastChangesFromGitea.Count == 0)
{
return 0;
}
var chatRequest = lastChangesFromGitea.Select(x => new ChatRequest(x.Key, x.Value));
var chatRequest = lastChangesFromGitea.Select(x => new ChatRequest(x.Value.Item1, x.Key, x.Value.Item2));
var promptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Prompts", "ChangesChecker.txt");
var prompt = await File.ReadAllTextAsync(promptPath, cancellationToken).ConfigureAwait(false);
var prompt = await File.ReadAllTextAsync(promptPath).ConfigureAwait(false);
var decisionDoc = await openAiService.GetResponseFromChat(chatRequest, prompt).ConfigureAwait(false);
if (decisionDoc is null)
if (decisionDoc == null)
{
_logger.LogWarning("AIGate: no response from LLM, continuing by default.");
Console.WriteLine("AIGate: no response from LLM, continuing by default.");
return 0;
}
if (!decisionDoc.RootElement.TryGetProperty("decision", out var decisionElement) ||
decisionElement.ValueKind == JsonValueKind.Null)
{
_logger.LogWarning("AIGate: decision property missing or null in LLM response, continuing by default.");
Console.WriteLine("AIGate: decision property missing or null in LLM response, continuing by default.");
return 0;
}
@@ -57,71 +54,66 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
}
var content = GetFormattedJsonRequest(changesElement);
var (isSuccess, response) = await giteaService.SendRequestAsync(giteaConfiguration, "contents", content)
var (status, response) = await giteaService.SendRequestAsync(giteaConfiguration, "contents", content)
.ConfigureAwait(false);
if (isSuccess)
if (status)
{
_logger.LogInformation("AIGate: created PR with suggested changes.");
_logger.LogInformation(response);
Console.WriteLine("AIGate: created PR with suggested changes.");
Console.WriteLine(response);
}
else
{
_logger.LogError("AIGate: failed to create PR with suggested changes.");
_logger.LogError(response);
Console.WriteLine("AIGate: failed to create PR with suggested changes.");
Console.WriteLine(response);
}
return 0;
}
catch (OperationCanceledException)
{
_logger.LogInformation("AIGate: operation was canceled.");
Console.WriteLine("AIGate: operation was canceled.");
return 0;
}
catch (Exception ex)
{
_logger.LogError(ex, "AIGate: while processing: {Message}", ex.Message);
Console.WriteLine($"AIGate: while processing: {ex.Message}");
return 0;
}
}
private async Task<Dictionary<string, string>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient, GiteaConfiguration giteaConfiguration, CancellationToken cancellationToken = default)
private async Task<Dictionary<string, (string, string)>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient,
GiteaConfiguration giteaConfiguration)
{
var lastChanges = new Dictionary<string, string>(StringComparer.Ordinal);
var allBranches = await giteaService.GetAllBranchesAsync(giteaClient, giteaConfiguration).ConfigureAwait(false);
var lastUpdatedBranch = allBranches
.Where(x => x.Name != "master" && x.Name.StartsWith("feature/"))
.OrderByDescending(x => x.Commit.Timestamp)
var lastChanges = new Dictionary<string, (string, string)>(StringComparer.Ordinal);
var lastUpdatedBranch =
(await giteaService.GetAllBranchesAsync(giteaClient, giteaConfiguration).ConfigureAwait(false))
.Where(x => x.Name != "master" && x.Name.StartsWith("feature/")).OrderByDescending(x => x.Commit.Timestamp)
.FirstOrDefault();
if (lastUpdatedBranch is null ||
if (lastUpdatedBranch == null ||
lastUpdatedBranch.Commit.Message.Contains("LLM: Code review suggestions", StringComparison.Ordinal))
{
return lastChanges;
}
var lastCommit = await giteaService
.GetCommitByIdAsync(giteaClient, giteaConfiguration, lastUpdatedBranch.Commit.Id)
.ConfigureAwait(false);
.GetCommitByIdAsync(giteaClient, giteaConfiguration, lastUpdatedBranch.Commit.Id).ConfigureAwait(false);
if (lastCommit is null)
if (lastCommit == null)
{
return lastChanges;
}
foreach (CommitAffectedFiles commitAffectedFile in lastCommit.Files)
{
cancellationToken.ThrowIfCancellationRequested();
var fileContent = await giteaService
.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename, lastUpdatedBranch.Name)
.ConfigureAwait(false);
.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename,
lastUpdatedBranch.Name).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(fileContent.Content) && !lastChanges.ContainsKey(commitAffectedFile.Filename))
if (!string.IsNullOrEmpty(fileContent.Content) && !lastChanges.ContainsKey(commitAffectedFile.Filename))
{
lastChanges.Add(commitAffectedFile.Filename, Base64Decode(fileContent.Content));
lastChanges.Add(commitAffectedFile.Filename, (lastUpdatedBranch.Name, Base64Decode(fileContent.Content)));
}
}
@@ -138,50 +130,41 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
? repoSpan.Slice(secondLastSlash + 1, lastSlash - secondLastSlash - 1).ToString()
: string.Empty;
string repository = lastSlash >= 0 ? repoSpan.Slice(lastSlash + 1).ToString() : repositoryPath;
const string defaultBranch = "master";
const string defaultHost = "https://git.modwad.pl";
const string branch = "master";
const string host = "https://git.modwad.pl";
return new GiteaConfiguration
{
Owner = owner,
Repository = repository,
Branch = defaultBranch,
Branch = branch,
ApiToken = giteaApiToken,
Host = defaultHost
Host = host
};
}
private string Base64Decode(string base64EncodedData)
{
try
{
byte[] bytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(bytes);
}
catch (FormatException ex)
{
_logger.LogWarning(ex, "Invalid Base64 data encountered.");
return string.Empty;
}
byte[] bytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(bytes);
}
private StringContent GetFormattedJsonRequest(JsonElement json)
{
string branch = json.GetProperty("branch").GetString() ?? string.Empty;
string newBranch = json.GetProperty("new_branch").GetString() ?? string.Empty;
string message = json.GetProperty("message").GetString() ?? string.Empty;
var branch = json.GetProperty("branch").GetString();
var newBranch = json.GetProperty("new_branch").GetString();
var message = json.GetProperty("message").GetString();
var filesJson = json.GetProperty("files");
var files = new List<object>(filesJson.GetArrayLength());
foreach (var fileEl in filesJson.EnumerateArray())
{
string operation = fileEl.GetProperty("operation").GetString() ?? string.Empty;
string path = fileEl.GetProperty("path").GetString() ?? string.Empty;
string contentPlain = fileEl.GetProperty("content").GetString() ?? string.Empty;
var operation = fileEl.GetProperty("operation").GetString();
var path = fileEl.GetProperty("path").GetString();
var contentPlain = fileEl.GetProperty("content").GetString() ?? string.Empty;
string contentBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(contentPlain));
var contentBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(contentPlain));
files.Add(new
{
@@ -199,7 +182,7 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
files
};
string jsonPayload = JsonSerializer.Serialize(payload);
var jsonPayload = JsonSerializer.Serialize(payload);
return new StringContent(jsonPayload, Encoding.UTF8, "application/json");
}