LLM: Code review suggestions plus what was changed:
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
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:
@@ -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("AI‑Gate: no response from LLM, continuing by default.");
|
Console.WriteLine("AI–Gate: 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("AI–Gate: 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($"AI‑Gate: created PR with suggested changes.");
|
Console.WriteLine("AI–Gate: created PR with suggested changes.");
|
||||||
Console.WriteLine(response);
|
Console.WriteLine(response);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("AI‑Gate: failed to create PR with suggested changes.");
|
Console.WriteLine("AI–Gate: failed to create PR with suggested changes.");
|
||||||
Console.WriteLine(response);
|
Console.WriteLine(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("AI–Gate: operation was canceled.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"AI‑Gate: while processing: {ex.Message}");
|
Console.WriteLine($"AI–Gate: 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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user