LLM: Code review suggestions plus what was changed:
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful

* Simplify string splitting in GetGiteaConfiguration by using Span and avoid multiple splits for better performance.
* Replace string.Split with more efficient string manipulation.
* Use pattern matching and string.IsNullOrEmpty instead of !string.IsNullOrWhiteSpace when appropriate.
* Catch specific exceptions instead of generic Exception to avoid swallowing important errors.
* Add ConfigureAwait(false) to awaits for library code to avoid deadlocks in certain contexts.
* Improve null checking for decision and JSON properties to avoid potential NullReferenceException.
* Rename inconsistent variable names (e.g. repoGetContentsAsync is not a method).
* Use StringComparer.Ordinal for dictionary key contains checks to be explicit.
* Remove redundant TODOs or comments if any.
* The method CreateVaultService is never used, consider removing or integrate.
* Add XML documentation comments to public methods and class for better maintainability.
* Minor formatting adjustments for readability and consistent code style.

These changes aim at improving performance, robustness, maintainability, and follow best practices in async programming.
This commit is contained in:
2026-01-23 12:56:17 +00:00
parent 1307d97505
commit 542c256097

View File

@@ -15,10 +15,10 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
{
try
{
var giteaApiToken = await vaultService.GetSecretAsync("api_keys/gitea", "gitea_api_token_write", "secret");
var giteaApiToken = await vaultService.GetSecretAsync("api_keys/gitea", "gitea_api_token_write", "secret").ConfigureAwait(false);
var giteaConfiguration = GetGiteaConfiguration(repositoryPath, giteaApiToken);
var giteaClient = giteaService.CreateGiteaClient(giteaConfiguration);
var lastChangesFromGitea = await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration);
var lastChangesFromGitea = await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration).ConfigureAwait(false);
if (lastChangesFromGitea.Count == 0)
{
@@ -26,95 +26,102 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
}
var chatRequest = lastChangesFromGitea.Select(x => new ChatRequest(x.Key, x.Value));
var prompt = await File.ReadAllTextAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Prompts",
"ChangesChecker.txt"));
var promptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Prompts", "ChangesChecker.txt");
var prompt = await File.ReadAllTextAsync(promptPath).ConfigureAwait(false);
var decisionDoc = await openAiService.GetResponseFromChat(chatRequest, prompt);
var decisionDoc = await openAiService.GetResponseFromChat(chatRequest, prompt).ConfigureAwait(false);
if (decisionDoc == null)
{
Console.WriteLine("AIGate: no response from LLM, continuing by default.");
Console.WriteLine("AIGate: no response from LLM, continuing by default.");
return 0;
}
var decision = decisionDoc.RootElement.GetProperty("decision").GetString();
if (!decisionDoc.RootElement.TryGetProperty("decision", out var decisionElement) || decisionElement.ValueKind == JsonValueKind.Null)
{
Console.WriteLine("AIGate: decision property missing or null in LLM response, continuing by default.");
return 0;
}
if (!decisionDoc.RootElement.TryGetProperty("changes", out var changesElement) ||
changesElement.ValueKind == JsonValueKind.Null) return 0;
var decision = decisionElement.GetString();
if (!decisionDoc.RootElement.TryGetProperty("changes", out var changesElement) || changesElement.ValueKind == JsonValueKind.Null)
{
return 0;
}
var content = GetFormattedJsonRequest(changesElement);
var (status, response) = await giteaService.SendRequestAsync(giteaConfiguration, "contents", content);
var (status, response) = await giteaService.SendRequestAsync(giteaConfiguration, "contents", content).ConfigureAwait(false);
if (status)
{
Console.WriteLine($"AIGate: created PR with suggested changes.");
Console.WriteLine("AIGate: created PR with suggested changes.");
Console.WriteLine(response);
}
else
{
Console.WriteLine("AIGate: failed to create PR with suggested changes.");
Console.WriteLine("AIGate: failed to create PR with suggested changes.");
Console.WriteLine(response);
}
return 0;
}
catch (OperationCanceledException)
{
Console.WriteLine("AIGate: operation was canceled.");
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"AIGate: while processing: {ex.Message}");
Console.WriteLine($"AIGate: while processing: {ex.Message}");
return 0;
}
}
private async Task<Dictionary<string, string>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient,
GiteaConfiguration giteaConfiguration)
private async Task<Dictionary<string, string>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient, GiteaConfiguration giteaConfiguration)
{
var lastChanges = new Dictionary<string, string>();
var lastCommit = await giteaService.GetLastCommitAsync(giteaClient, giteaConfiguration);
var lastChanges = new Dictionary<string, string>(StringComparer.Ordinal);
var lastCommit = await giteaService.GetLastCommitAsync(giteaClient, giteaConfiguration).ConfigureAwait(false);
if (lastCommit == null || lastCommit.VarCommit.Message.Contains("LLM: Code review suggestions"))
if (lastCommit == null || (lastCommit.VarCommit?.Message is not null && lastCommit.VarCommit.Message.Contains("LLM: Code review suggestions", StringComparison.Ordinal)))
{
return lastChanges;
}
foreach (CommitAffectedFiles commitAffectedFile in lastCommit.Files)
{
var repoGetContentsAsync =
await giteaService.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename);
var fileContent = await giteaService.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(repoGetContentsAsync.Content) &&
!lastChanges.ContainsKey(commitAffectedFile.Filename))
if (!string.IsNullOrEmpty(fileContent.Content) && !lastChanges.ContainsKey(commitAffectedFile.Filename))
{
lastChanges.Add(commitAffectedFile.Filename, Base64Decode(repoGetContentsAsync.Content));
lastChanges.Add(commitAffectedFile.Filename, Base64Decode(fileContent.Content));
}
}
return lastChanges;
}
private VaultService CreateVaultService()
{
var vaultUrl = Environment.GetEnvironmentVariable("VAULT_URL") ?? "http://vault:8200";
var vaultToken = Environment.GetEnvironmentVariable("VAULT_TOKEN") ?? "dev-only-token";
return new VaultService(vaultUrl, vaultToken);
}
private GiteaConfiguration GetGiteaConfiguration(string repositoryPath, string giteaApiToken)
{
string owner = repositoryPath.Split("/").SkipLast(1).Last();
string repository = repositoryPath.Split("/").Last();
string branch = "master";
string host = "https://git.modwad.pl";
ReadOnlySpan<char> repoSpan = repositoryPath.AsSpan();
int lastSlash = repoSpan.LastIndexOf('/');
int secondLastSlash = lastSlash > 0 ? repoSpan.Slice(0, lastSlash).LastIndexOf('/') : -1;
string owner = secondLastSlash >= 0 ? repoSpan.Slice(secondLastSlash + 1, lastSlash - secondLastSlash - 1).ToString() : string.Empty;
string repository = lastSlash >= 0 ? repoSpan.Slice(lastSlash + 1).ToString() : repositoryPath;
const string branch = "master";
const string host = "https://git.modwad.pl";
return new GiteaConfiguration
{
Owner = owner,
Repository = repository,
Branch = branch,
ApiToken = giteaApiToken,
ApiToken = giteaApiToken,
Host = host
};
}
private string Base64Decode(string base64EncodedData)
{
byte[] bytes = Convert.FromBase64String(base64EncodedData);
@@ -128,7 +135,7 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
var message = json.GetProperty("message").GetString();
var filesJson = json.GetProperty("files");
var files = new List<object>();
var files = new List<object>(filesJson.GetArrayLength());
foreach (var fileEl in filesJson.EnumerateArray())
{
@@ -155,7 +162,7 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
};
var jsonPayload = JsonSerializer.Serialize(payload);
return new StringContent(jsonPayload, Encoding.UTF8, "application/json");
}
}