add error handling/logging

This commit is contained in:
Kosta Petan 2023-11-09 16:03:38 +00:00
parent d18296f800
commit 45d9282ab2
11 changed files with 420 additions and 269 deletions

View File

@ -5,6 +5,7 @@ using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Reliability.Basic;
using Octokit.Webhooks;
using Octokit.Webhooks.AspNetCore;
using Orleans.Configuration;
@ -17,7 +18,8 @@ builder.Services.AddHttpClient();
builder.Services.AddSingleton(s =>
{
var ghOptions = s.GetService<IOptions<GithubOptions>>();
var ghService = new GithubAuthService(ghOptions);
var logger = s.GetService<ILogger<GithubAuthService>>();
var ghService = new GithubAuthService(ghOptions, logger);
var client = ghService.GetGitHubClient().Result;
return client;
});
@ -160,5 +162,9 @@ static IKernel CreateKernel(IServiceProvider provider)
return new KernelBuilder()
.WithLoggerFactory(loggerFactory)
.WithAzureChatCompletionService(openAiConfig.DeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey, true, openAiConfig.ServiceId, true)
.WithRetryBasic(new BasicRetryConfig {
MaxRetryCount = 5,
UseExponentialBackoff = true
})
.WithMemory(semanticTextMemory).Build();
}

View File

@ -211,7 +211,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
Org = org,
Repo = repo,
Number = (int)issueNumber,
Content = readme
Content = string.IsNullOrEmpty(readme)? "Sorry, something went wrong": readme
});
}
else if (skillName == nameof(DevLead) && functionName == nameof(DevLead.Plan))
@ -223,19 +223,20 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
Org = org,
Repo = repo,
Number = (int)issueNumber,
Content = plan
Content = string.IsNullOrEmpty(plan)? "Sorry, something went wrong":plan
});
}
else if (skillName == nameof(Developer) && functionName == nameof(Developer.Implement))
{
var dev = _grains.GetGrain<IDevelopCode>(issueNumber, suffix);
var code = await dev.GenerateCode(input);
await _ghService.PostComment(new PostCommentRequest
{
Org = org,
Repo = repo,
Number = (int)issueNumber,
Content = code
Content = string.IsNullOrEmpty(code)? "Sorry, something went wrong":code
});
}
else { }// something went wrong

View File

@ -37,8 +37,6 @@ public class Conductor : Grain, IOrchestrateWorkflows
ParentNumber = parentNumber
});
var suffix = $"{org}-{repo}";
var pm = GrainFactory.GetGrain<IManageProduct>(pmIssue.IssueNumber, suffix);
var devLead = GrainFactory.GetGrain<ILeadDevelopment>(devLeadIssue.IssueNumber, suffix);
var lookup = GrainFactory.GetGrain<ILookupMetadata>(suffix);
var metadataList = new List<StoreMetadataPairs>{
@ -81,9 +79,4 @@ public class Conductor : Grain, IOrchestrateWorkflows
}
await lookup.StoreMetadata(metadataList);
}
public Task ScheduleCommitSandboxRun(CommitRequest commitRequest, MarkTaskCompleteRequest markTaskCompleteRequest)
{
throw new NotImplementedException();
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AI.DevTeam.Skills;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.Orchestration;
@ -9,20 +10,19 @@ namespace Microsoft.AI.DevTeam;
public class DeveloperLead : SemanticPersona, ILeadDevelopment
{
private readonly IKernel _kernel;
private readonly ILogger<DeveloperLead> _logger;
protected override string MemorySegment => "dev-lead-memory";
public DeveloperLead(IKernel kernel, [PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state) : base(state)
public DeveloperLead(IKernel kernel, [PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, ILogger<DeveloperLead> logger) : base(state)
{
_kernel = kernel;
_logger = logger;
}
public async Task<string> CreatePlan(string ask)
{
// var architectId = Guid.NewGuid();
// var plan = "this is my plan";
// var architect = GrainFactory.GetGrain<IArchitectSolutions>(architectId);
// var review = architect.ReviewPlan(plan);
// return Task.FromResult(plan);
try
{
var function = _kernel.CreateSemanticFunction(DevLead.Plan, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.4, TopP = 1 });
var context = new ContextVariables();
context.Set("input", ask);
@ -48,6 +48,12 @@ public class DeveloperLead : SemanticPersona, ILeadDevelopment
return resultMessage;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating development plan");
return default;
}
}
public Task<DevLeadPlanResponse> GetLatestPlan()
{
@ -55,11 +61,6 @@ public class DeveloperLead : SemanticPersona, ILeadDevelopment
var response = JsonSerializer.Deserialize<DevLeadPlanResponse>(plan);
return Task.FromResult(response);
}
public Task<string> BuildUnderstanding(string content)
{
throw new NotImplementedException();
}
}
[GenerateSerializer]

View File

@ -1,6 +1,6 @@
namespace Microsoft.AI.DevTeam;
public interface ILeadDevelopment: IGrainWithIntegerCompoundKey, IChatHistory, IUnderstand
public interface ILeadDevelopment: IGrainWithIntegerCompoundKey, IChatHistory
{
Task<string> CreatePlan(string ask);
Task<DevLeadPlanResponse> GetLatestPlan();

View File

@ -1,4 +1,5 @@
using Microsoft.AI.DevTeam.Skills;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.Orchestration;
@ -9,14 +10,19 @@ namespace Microsoft.AI.DevTeam;
public class Dev : SemanticPersona, IDevelopCode
{
private readonly IKernel _kernel;
private readonly ILogger<Dev> _logger;
protected override string MemorySegment => "dev-memory";
public Dev(IKernel kernel, [PersistentState("state", "messages")]IPersistentState<SemanticPersonaState> state) : base(state)
public Dev(IKernel kernel, [PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, ILogger<Dev> logger) : base(state)
{
_kernel = kernel;
_logger = logger;
}
public async Task<string> GenerateCode(string ask)
{
try
{
var function = _kernel.CreateSemanticFunction(Developer.Implement, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.8, TopP = 1 });
var context = new ContextVariables();
@ -42,6 +48,12 @@ public class Dev : SemanticPersona, IDevelopCode
return resultMessage;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error generating code");
return default;
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AI.DevTeam.Skills;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.Orchestration;
@ -8,13 +9,18 @@ namespace Microsoft.AI.DevTeam;
public class ProductManager : SemanticPersona, IManageProduct
{
private readonly IKernel _kernel;
private readonly ILogger<ProductManager> _logger;
protected override string MemorySegment => "pm-memory";
public ProductManager(IKernel kernel,[PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state) : base(state)
public ProductManager(IKernel kernel, [PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, ILogger<ProductManager> logger) : base(state)
{
_kernel = kernel;
_logger = logger;
}
public async Task<string> CreateReadme(string ask)
{
try
{
var function = _kernel.CreateSemanticFunction(PM.Readme, new OpenAIRequestSettings { MaxTokens = 10000, Temperature = 0.6, TopP = 1 });
var context = new ContextVariables();
@ -40,4 +46,10 @@ public class ProductManager : SemanticPersona, IManageProduct
await _state.WriteStateAsync();
return resultMessage;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating readme");
return default;
}
}
}

View File

@ -7,6 +7,7 @@ using Azure.ResourceManager.ContainerInstance;
using Azure.ResourceManager.ContainerInstance.Models;
using Azure.ResourceManager.Resources;
using Azure.Storage.Files.Shares;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -15,13 +16,17 @@ namespace Microsoft.AI.DevTeam;
public class AzureService : IManageAzure
{
private readonly AzureOptions _azSettings;
private readonly ILogger<AzureService> _logger;
public AzureService(IOptions<AzureOptions> azOptions)
public AzureService(IOptions<AzureOptions> azOptions, ILogger<AzureService> logger)
{
_azSettings = azOptions.Value;
_logger = logger;
}
public async Task DeleteSandbox(string sandboxId)
{
try
{
var client = new ArmClient(new DefaultAzureCredential());
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
@ -31,8 +36,16 @@ public class AzureService : IManageAzure
var containerGroup = await collection.GetAsync(sandboxId);
await containerGroup.Value.DeleteAsync(WaitUntil.Started);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting sandbox");
}
}
public async Task<bool> IsSandboxCompleted(string sandboxId)
{
try
{
var client = new ArmClient(new DefaultAzureCredential());
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
@ -43,8 +56,16 @@ public class AzureService : IManageAzure
return containerGroup.Value.Data.ProvisioningState == "Succeeded"
&& containerGroup.Value.Data.Containers.First().InstanceView.CurrentState.State == "Terminated";
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking sandbox status");
return false;
}
}
public async Task RunInSandbox(SandboxRequest request)
{
try
{
var client = string.IsNullOrEmpty(_azSettings.ManagedIdentity) ?
new ArmClient(new AzureCliCredential())
@ -85,8 +106,16 @@ public class AzureService : IManageAzure
};
await collection.CreateOrUpdateAsync(WaitUntil.Completed, runId, data);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error running sandbox");
}
}
public async Task Store(SaveOutputRequest request)
{
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
var parentDirName = $"{request.Directory}/{request.Org}-{request.Repo}";
@ -118,6 +147,12 @@ public class AzureService : IManageAzure
stream);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error storing output");
}
}
}
public interface IManageAzure

View File

@ -1,6 +1,7 @@
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Octokit.Internal;
@ -14,15 +15,19 @@ public class CodeAnalyzer : IAnalyzeCode
{
private readonly ServiceOptions _serviceOptions;
private readonly HttpClient _httpClient;
private readonly ILogger<CodeAnalyzer> _logger;
public CodeAnalyzer(IOptions<ServiceOptions> serviceOptions, HttpClient httpClient)
public CodeAnalyzer(IOptions<ServiceOptions> serviceOptions, HttpClient httpClient, ILogger<CodeAnalyzer> logger)
{
_serviceOptions = serviceOptions.Value;
_httpClient = httpClient;
_logger = logger;
_httpClient.BaseAddress = new Uri(_serviceOptions.IngesterUrl);
}
public async Task<IEnumerable<CodeAnalysis>> Analyze(string content)
{
try
{
var request = new CodeAnalysisRequest { Content = content };
var body = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
@ -31,6 +36,12 @@ public class CodeAnalyzer : IAnalyzeCode
var result = JsonSerializer.Deserialize<IEnumerable<CodeAnalysis>>(stringResult);
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error analyzing code");
return Enumerable.Empty<CodeAnalysis>();
}
}
}
public class CodeAnalysisRequest

View File

@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Octokit;
@ -5,12 +6,16 @@ namespace Microsoft.AI.DevTeam;
public class GithubAuthService
{
private readonly GithubOptions _githubSettings;
private readonly ILogger<GithubAuthService> _logger;
public GithubAuthService(IOptions<GithubOptions> ghOptions)
public GithubAuthService(IOptions<GithubOptions> ghOptions, ILogger<GithubAuthService> logger)
{
_githubSettings = ghOptions.Value;
_logger = logger;
}
public async Task<GitHubClient> GetGitHubClient()
{
try
{
// Use GitHubJwt library to create the GitHubApp Jwt Token using our private certificate PEM file
var generator = new GitHubJwt.GitHubJwtFactory(
@ -33,4 +38,10 @@ public class GithubAuthService
Credentials = new Credentials(response.Token)
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting GitHub client");
return default;
}
}
}

View File

@ -24,6 +24,8 @@ public class GithubService : IManageGithub
}
public async Task CommitToBranch(CommitRequest request)
{
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
@ -67,21 +69,43 @@ public class GithubService : IManageGithub
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error committing to branch");
}
}
public async Task CreateBranch(CreateBranchRequest request)
{
try
{
var ghRepo = await _ghClient.Repository.Get(request.Org, request.Repo);
await _ghClient.Git.Reference.CreateBranch(request.Org, request.Repo, request.Branch, ghRepo.DefaultBranch);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating branch");
}
}
public async Task<string> GetMainLanguage(string org, string repo)
{
try
{
var languages = await _ghClient.Repository.GetAllLanguages(org, repo);
var mainLanguage = languages.OrderByDescending(l => l.NumberOfBytes).First();
return mainLanguage.Name;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting main language");
return default;
}
}
public async Task<NewIssueResponse> CreateIssue(CreateIssueRequest request)
{
try
{
var newIssue = new NewIssue($"{request.Label} chain for #{request.ParentNumber}")
{
@ -97,34 +121,73 @@ public class GithubService : IManageGithub
IssueNumber = issue.Number,
CommentId = comment.Id
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating issue");
return default;
}
}
public async Task CreatePR(CreatePRRequest request)
{
try
{
var ghRepo = await _ghClient.Repository.Get(request.Org, request.Repo);
await _ghClient.PullRequest.Create(request.Org, request.Repo, new NewPullRequest($"New app #{request.Number}", request.Branch, ghRepo.DefaultBranch));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating PR");
}
}
public async Task MarkTaskComplete(MarkTaskCompleteRequest request)
{
try
{
var comment = await _ghClient.Issue.Comment.Get(request.Org, request.Repo, request.CommentId);
var updatedComment = comment.Body.Replace("[ ]", "[x]");
await _ghClient.Issue.Comment.Update(request.Org, request.Repo, request.CommentId, updatedComment);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error marking task complete");
}
}
public async Task PostComment(PostCommentRequest request)
{
try
{
await _ghClient.Issue.Comment.Create(request.Org, request.Repo, request.Number, request.Content);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error posting comment");
}
}
public async Task<IEnumerable<FileResponse>> GetFiles(string org, string repo, string branch, Func<RepositoryContent, bool> filter)
{
try
{
var items = await _ghClient.Repository.Content.GetAllContentsByRef(org, repo, branch);
return await CollectFiles(org, repo, branch, items, filter);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting files");
return Enumerable.Empty<FileResponse>();
}
}
private async Task<IEnumerable<FileResponse>> CollectFiles(string org, string repo, string branch, IReadOnlyList<RepositoryContent> items, Func<RepositoryContent, bool> filter)
{
try
{
var result = new List<FileResponse>();
foreach (var item in items)
@ -146,6 +209,12 @@ public class GithubService : IManageGithub
}
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error collecting files");
return Enumerable.Empty<FileResponse>();
}
}
}
public class FileResponse