This commit is contained in:
Kosta Petan
2024-02-23 15:12:39 +01:00
parent 0e84879496
commit 246639912d
8 changed files with 84 additions and 265 deletions

View File

@@ -78,7 +78,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
var skillName = labels.Keys.Where(k=>k != "Parent").FirstOrDefault();
long? parentNumber = labels.ContainsKey("Parent") ? long.Parse(labels["Parent"]) : null;
var suffix = $"{org}-{repo}";
// we only resond to non-bot comments
// we only respond to non-bot comments
if (issueCommentEvent.Sender.Type.Value != UserType.Bot)
{
await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], suffix, input, org, repo);

View File

@@ -1,3 +1,5 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Orchestration;
using Orleans.Runtime;
@@ -5,62 +7,52 @@ using Orleans.Streams;
namespace Microsoft.AI.DevTeam;
public abstract class Agent : Grain, IChatHistory, IGrainWithStringKey
public abstract class Agent : Grain, IGrainWithStringKey
{
public Agent(
[PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state)
[PersistentState("state", "messages")] IPersistentState<AgentState> state)
{
_state = state;
}
protected virtual string MemorySegment { get; set; }
protected List<ChatHistoryItem> History { get; set; }
protected readonly IPersistentState<SemanticPersonaState> _state;
protected readonly IPersistentState<AgentState> _state;
public abstract Task HandleEvent(Event item, StreamSequenceToken? token);
public async Task<string> GetLastMessage()
{
return _state.State.History.Last().Message;
}
protected async Task AddWafContext(ISemanticTextMemory memory, string ask, ContextVariables context)
protected async Task<ContextVariables> CreateWafContext(ISemanticTextMemory memory, string ask)
{
var context = new ContextVariables();
var interestingMemories = memory.SearchAsync("waf-pages", ask, 2);
var wafContext = "Consider the following architectural guidelines:";
await foreach (var m in interestingMemories)
{
wafContext += $"\n {m.Metadata.Text}";
}
context.Set("input", ask);
context.Set("wafContext", wafContext);
return context;
}
// public async override Task OnActivateAsync(CancellationToken cancellationToken)
// {
// var streamProvider = this.GetStreamProvider("StreamProvider");
// var streamId = StreamId.Create("DevPersonas", this.GetPrimaryKey());
// var stream = streamProvider.GetStream<Event>(streamId);
protected void AddToHistory(string message, ChatUserType userType)
{
if (_state.State.History == null) _state.State.History = new List<ChatHistoryItem>();
_state.State.History.Add(new ChatHistoryItem
{
Message = message,
Order = _state.State.History.Count + 1,
UserType = userType
});
}
// await stream.SubscribeAsync(HandleEvent);
// }
}
public interface IChatHistory
{
Task<string> GetLastMessage();
}
public interface IUnderstand
{
Task<UnderstandingResult> BuildUnderstanding(string content);
}
[GenerateSerializer]
public class UnderstandingResult {
[Id(0)]
public string NewUnderstanding { get; set; }
[Id(1)]
public string Explanation { get; set; }
protected async Task<string> CallFunction(string template, string ask, IKernel kernel, ISemanticTextMemory memory)
{
var function = kernel.CreateSemanticFunction(template, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.8, TopP = 1 });
var context = await CreateWafContext(memory, ask);
var result = (await kernel.RunAsync(context, function)).ToString();
AddToHistory(ask, ChatUserType.User);
AddToHistory(result, ChatUserType.Agent);
await _state.WriteStateAsync();
return result;
}
}
[Serializable]
@@ -72,7 +64,7 @@ public class ChatHistoryItem
}
public class SemanticPersonaState
public class AgentState
{
public List<ChatHistoryItem> History { get; set; }
public string Understanding { get; set; }

View File

@@ -1,31 +0,0 @@
using Orleans.Runtime;
using Orleans.Streams;
namespace Microsoft.AI.DevTeam;
public class Architect : Agent
{
public Architect( [PersistentState("state", "messages")]IPersistentState<SemanticPersonaState> state) : base(state)
{
}
public Task<string> GenerateProjectStructure(string ask)
{
throw new NotImplementedException();
}
public Task<string> ReviewPlan(string plan)
{
throw new NotImplementedException();
}
public async override Task HandleEvent(Event item, StreamSequenceToken? token)
{
switch (item.Type)
{
case EventType.NewAsk:
break;
default:
break;
}
}
}

View File

@@ -16,50 +16,21 @@ public class Dev : Agent
private readonly IKernel _kernel;
private readonly ISemanticTextMemory _memory;
private readonly ILogger<Dev> _logger;
private readonly IManageGithub _ghService;
protected override string MemorySegment => "dev-memory";
public Dev([PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<Dev> logger, IManageGithub ghService) : base(state)
public Dev([PersistentState("state", "messages")] IPersistentState<AgentState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<Dev> logger) : base(state)
{
_kernel = kernel;
_memory = memory;
_logger = logger;
_ghService = ghService;
}
public async Task<string> GenerateCode(string ask)
public async override Task OnActivateAsync(CancellationToken cancellationToken)
{
try
{
var function = _kernel.CreateSemanticFunction(Developer.Implement, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.8, TopP = 1 });
var context = new ContextVariables();
if (_state.State.History == null) _state.State.History = new List<ChatHistoryItem>();
_state.State.History.Add(new ChatHistoryItem
{
Message = ask,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.User
});
await AddWafContext(_memory, ask, context);
context.Set("input", ask);
var streamProvider = this.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create("DevPersonas", this.GetPrimaryKeyString());
var stream = streamProvider.GetStream<Event>(streamId);
var result = await _kernel.RunAsync(context, function);
var resultMessage = result.ToString();
_state.State.History.Add(new ChatHistoryItem
{
Message = resultMessage,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.Agent
});
await _state.WriteStateAsync();
return resultMessage;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating code");
return default;
}
await stream.SubscribeAsync(HandleEvent);
}
public async override Task HandleEvent(Event item, StreamSequenceToken? token)
@@ -68,7 +39,7 @@ public class Dev : Agent
{
case EventType.CodeGenerationRequested:
var code = await GenerateCode(item.Message);
await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), code);
//await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), code);
// postEvent EventType.CodeGenerated
break;
case EventType.ChainClosed:
@@ -79,8 +50,19 @@ public class Dev : Agent
break;
}
}
public async Task<string> GenerateCode(string ask)
{
try
{
return await CallFunction(Developer.Implement, ask, _kernel, _memory);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating code");
return default;
}
}
public Task<string> ReviewPlan(string plan)
{
throw new NotImplementedException();
@@ -165,4 +147,13 @@ public class Dev : Agent
return default;
}
}
}
[GenerateSerializer]
public class UnderstandingResult
{
[Id(0)]
public string NewUnderstanding { get; set; }
[Id(1)]
public string Explanation { get; set; }
}

View File

@@ -18,9 +18,7 @@ public class DeveloperLead : Agent
private readonly IManageGithub _ghService;
protected override string MemorySegment => "dev-lead-memory";
public DeveloperLead([PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<DeveloperLead> logger, IManageGithub ghService) : base(state)
public DeveloperLead([PersistentState("state", "messages")] IPersistentState<AgentState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<DeveloperLead> logger, IManageGithub ghService) : base(state)
{
_kernel = kernel;
_memory = memory;
@@ -35,34 +33,29 @@ public class DeveloperLead : Agent
await stream.SubscribeAsync(HandleEvent);
}
public async override Task HandleEvent(Event item, StreamSequenceToken? token)
{
switch (item.Type)
{
case EventType.DevPlanRequested:
var plan = await CreatePlan(item.Message);
await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), plan);
// postEvent EventType.DevPlanGenerated
break;
case EventType.ChainClosed:
await ClosePlan(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), long.Parse(item.Data["parentNumber"]));
// postEvent EventType.DevPlanFinished
break;
default:
break;
}
}
public async Task<string> CreatePlan(string ask)
{
try
{
var function = _kernel.CreateSemanticFunction(DevLead.Plan, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.4, TopP = 1 });
var context = new ContextVariables();
context.Set("input", ask);
if (_state.State.History == null) _state.State.History = new List<ChatHistoryItem>();
_state.State.History.Add(new ChatHistoryItem
{
Message = ask,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.User
});
await AddWafContext(_memory, ask, context);
context.Set("input", ask);
var result = await _kernel.RunAsync(context, function);
var resultMessage = result.ToString();
_state.State.History.Add(new ChatHistoryItem
{
Message = resultMessage,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.Agent
});
await _state.WriteStateAsync();
return resultMessage;
return await CallFunction(DevLead.Plan, ask, _kernel, _memory);
}
catch (Exception ex)
{
@@ -107,23 +100,6 @@ public class DeveloperLead : Agent
var response = JsonSerializer.Deserialize<DevLeadPlanResponse>(plan);
return Task.FromResult(response);
}
public async override Task HandleEvent(Event item, StreamSequenceToken? token)
{
switch (item.Type)
{
case EventType.DevPlanRequested:
var plan = await CreatePlan(item.Message);
await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), plan);
// postEvent EventType.DevPlanGenerated
break;
case EventType.ChainClosed:
await ClosePlan(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), long.Parse(item.Data["parentNumber"]));
// postEvent EventType.DevPlanFinished
break;
default:
break;
}
}
}
[GenerateSerializer]

View File

@@ -1,54 +0,0 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Octokit;
using Orleans.Runtime;
using Orleans.Streams;
namespace Microsoft.AI.DevTeam;
public class Ingester : Agent
{
protected override string MemorySegment => "code-analysis";
private readonly IManageGithub _ghService;
private readonly IKernel _kernel;
private readonly ISemanticTextMemory _memory;
private readonly IAnalyzeCode _codeAnalyzer;
public Ingester([PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, IManageGithub ghService, IKernel kernel, ISemanticTextMemory memory, IAnalyzeCode codeAnalyzer) : base(state)
{
_ghService = ghService;
_kernel = kernel;
_memory = memory;
_codeAnalyzer = codeAnalyzer;
}
// public async Task IngestionFlow(string org, string repo, string branch)
// {
// var suffix = $"{org}-{repo}";
// var language = await _ghService.GetMainLanguage(org, repo);
// var files = await _ghService.GetFiles(org, repo, branch, Language.Filters[language]);
// //var dev = GrainFactory.GetGrain<IDevelopCode>(0, suffix);
// foreach (var file in files)
// {
// var codeAnalysis = await _codeAnalyzer.Analyze(file.Content);
// codeAnalysis.ToList().ForEach(async c =>
// await _memory.SaveInformationAsync(MemorySegment, c.CodeBlock, Guid.NewGuid().ToString(), c.Meaning));
// // TODO: do something with the result
// //await dev.BuildUnderstanding(file.Content);
// }
// }
public override Task HandleEvent(Event item, StreamSequenceToken? token)
{
throw new NotImplementedException();
}
}
public static class Language
{
public static Dictionary<string, Func<RepositoryContent, bool>> Filters = new Dictionary<string, Func<RepositoryContent, bool>> {
{"C#", f => f.Name.EndsWith(".cs") }
};
}

View File

@@ -16,16 +16,11 @@ public class ProductManager : Agent
private readonly ISemanticTextMemory _memory;
private readonly ILogger<ProductManager> _logger;
private readonly IManageGithub _ghService;
protected override string MemorySegment => "pm-memory";
public ProductManager([PersistentState("state", "messages")] IPersistentState<SemanticPersonaState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<ProductManager> logger, IManageGithub ghService) : base(state)
public ProductManager([PersistentState("state", "messages")] IPersistentState<AgentState> state, IKernel kernel, ISemanticTextMemory memory, ILogger<ProductManager> logger) : base(state)
{
_kernel = kernel;
_memory = memory;
_logger = logger;
_ghService = ghService;
}
public async override Task OnActivateAsync(CancellationToken cancellationToken)
@@ -42,7 +37,7 @@ public class ProductManager : Agent
{
case EventType.ReadmeRequested:
var readme = await CreateReadme(item.Message);
await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), readme);
//await _ghService.PostComment(item.Data["org"], item.Data["repo"], long.Parse(item.Data["issueNumber"]), readme);
// postEvent ReadmeGenerated
break;
case EventType.ChainClosed:
@@ -57,29 +52,7 @@ public class ProductManager : Agent
{
try
{
var function = _kernel.CreateSemanticFunction(PM.Readme, new OpenAIRequestSettings { MaxTokens = 10000, Temperature = 0.6, TopP = 1 });
var context = new ContextVariables();
context.Set("input", ask);
if (_state.State.History == null) _state.State.History = new List<ChatHistoryItem>();
_state.State.History.Add(new ChatHistoryItem
{
Message = ask,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.User
});
await AddWafContext(_memory, ask, context);
context.Set("input", ask);
var result = await _kernel.RunAsync(context, function);
var resultMessage = result.ToString();
_state.State.History.Add(new ChatHistoryItem
{
Message = resultMessage,
Order = _state.State.History.Count + 1,
UserType = ChatUserType.Agent
});
await _state.WriteStateAsync();
return resultMessage;
return await CallFunction(DevLead.Plan, ask, _kernel, _memory);
}
catch (Exception ex)
{

View File

@@ -1,28 +0,0 @@
using Orleans.Runtime;
using Orleans.Streams;
namespace Microsoft.AI.DevTeam;
public class Tester : Agent
{
public Tester(
[PersistentState("state", "messages")]IPersistentState<SemanticPersonaState> state) : base(state)
{
}
public override Task HandleEvent(Event item, StreamSequenceToken? token)
{
throw new NotImplementedException();
}
public Task<string> ReviewPlan(string plan)
{
throw new NotImplementedException();
}
public Task<string> TestCode(string ask)
{
throw new NotImplementedException();
}
}