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 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 giteaConfiguration = GetGiteaConfiguration(repositoryPath, giteaApiToken);
var giteaClient = giteaService.CreateGiteaClient(giteaConfiguration); var giteaClient = giteaService.CreateGiteaClient(giteaConfiguration);
var lastChangesFromGitea = await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration); var lastChangesFromGitea = await GetLastChangesFromGiteaAsync(giteaClient, giteaConfiguration).ConfigureAwait(false);
if (lastChangesFromGitea.Count == 0) 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 chatRequest = lastChangesFromGitea.Select(x => new ChatRequest(x.Key, x.Value));
var prompt = await File.ReadAllTextAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Prompts", var promptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Prompts", "ChangesChecker.txt");
"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) 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; 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) || var decision = decisionElement.GetString();
changesElement.ValueKind == JsonValueKind.Null) return 0;
if (!decisionDoc.RootElement.TryGetProperty("changes", out var changesElement) || changesElement.ValueKind == JsonValueKind.Null)
{
return 0;
}
var content = GetFormattedJsonRequest(changesElement); 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) if (status)
{ {
Console.WriteLine($"AIGate: created PR with suggested changes."); Console.WriteLine("AIGate: created PR with suggested changes.");
Console.WriteLine(response); Console.WriteLine(response);
} }
else else
{ {
Console.WriteLine("AIGate: failed to create PR with suggested changes."); Console.WriteLine("AIGate: failed to create PR with suggested changes.");
Console.WriteLine(response); Console.WriteLine(response);
} }
return 0; return 0;
} }
catch (OperationCanceledException)
{
Console.WriteLine("AIGate: operation was canceled.");
return 0;
}
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"AIGate: while processing: {ex.Message}"); Console.WriteLine($"AIGate: while processing: {ex.Message}");
return 0; return 0;
} }
} }
private async Task<Dictionary<string, string>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient, private async Task<Dictionary<string, string>> GetLastChangesFromGiteaAsync(RepositoryApi giteaClient, GiteaConfiguration giteaConfiguration)
GiteaConfiguration giteaConfiguration)
{ {
var lastChanges = new Dictionary<string, string>(); var lastChanges = new Dictionary<string, string>(StringComparer.Ordinal);
var lastCommit = await giteaService.GetLastCommitAsync(giteaClient, giteaConfiguration); 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; return lastChanges;
} }
foreach (CommitAffectedFiles commitAffectedFile in lastCommit.Files) foreach (CommitAffectedFiles commitAffectedFile in lastCommit.Files)
{ {
var repoGetContentsAsync = var fileContent = await giteaService.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename).ConfigureAwait(false);
await giteaService.GetFileContentAsync(giteaClient, giteaConfiguration, commitAffectedFile.Filename);
if (!string.IsNullOrWhiteSpace(repoGetContentsAsync.Content) && if (!string.IsNullOrEmpty(fileContent.Content) && !lastChanges.ContainsKey(commitAffectedFile.Filename))
!lastChanges.ContainsKey(commitAffectedFile.Filename))
{ {
lastChanges.Add(commitAffectedFile.Filename, Base64Decode(repoGetContentsAsync.Content)); lastChanges.Add(commitAffectedFile.Filename, Base64Decode(fileContent.Content));
} }
} }
return lastChanges; 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) private GiteaConfiguration GetGiteaConfiguration(string repositoryPath, string giteaApiToken)
{ {
string owner = repositoryPath.Split("/").SkipLast(1).Last(); ReadOnlySpan<char> repoSpan = repositoryPath.AsSpan();
string repository = repositoryPath.Split("/").Last(); int lastSlash = repoSpan.LastIndexOf('/');
string branch = "master"; int secondLastSlash = lastSlash > 0 ? repoSpan.Slice(0, lastSlash).LastIndexOf('/') : -1;
string host = "https://git.modwad.pl";
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 return new GiteaConfiguration
{ {
Owner = owner, Owner = owner,
Repository = repository, Repository = repository,
Branch = branch, Branch = branch,
ApiToken = giteaApiToken, ApiToken = giteaApiToken,
Host = host Host = host
}; };
} }
private string Base64Decode(string base64EncodedData) private string Base64Decode(string base64EncodedData)
{ {
byte[] bytes = Convert.FromBase64String(base64EncodedData); byte[] bytes = Convert.FromBase64String(base64EncodedData);
@@ -128,7 +135,7 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
var message = json.GetProperty("message").GetString(); var message = json.GetProperty("message").GetString();
var filesJson = json.GetProperty("files"); var filesJson = json.GetProperty("files");
var files = new List<object>(); var files = new List<object>(filesJson.GetArrayLength());
foreach (var fileEl in filesJson.EnumerateArray()) foreach (var fileEl in filesJson.EnumerateArray())
{ {
@@ -155,7 +162,7 @@ public class ChangesCheckerAgent(IOpenAiService openAiService, IVaultService vau
}; };
var jsonPayload = JsonSerializer.Serialize(payload); var jsonPayload = JsonSerializer.Serialize(payload);
return new StringContent(jsonPayload, Encoding.UTF8, "application/json"); return new StringContent(jsonPayload, Encoding.UTF8, "application/json");
} }
} }