remove Dapr sample/implementation (#345)

Co-authored-by: Kosta Petan <Kosta.Petan@microsoft.com>
This commit is contained in:
Kosta Petan
2024-08-08 16:17:41 +02:00
committed by GitHub
parent 4da05734de
commit a1221d46d2
36 changed files with 0 additions and 2133 deletions

View File

@@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{290F9824-BAD
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents", "src\Microsoft.AI.Agents\Microsoft.AI.Agents.csproj", "{B0BF1CD6-34E3-4ED4-9B2A-9B8781E9BE20}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents.Dapr", "src\Microsoft.AI.Agents.Dapr\Microsoft.AI.Agents.Dapr.csproj", "{1972846E-4C21-4E40-B448-D78B73806BD9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents.Orleans", "src\Microsoft.AI.Agents.Orleans\Microsoft.AI.Agents.Orleans.csproj", "{A4AE4656-4919-45E2-9680-C317FBCF7693}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{943853E7-513D-45EA-870F-549CFC0AF8E8}"
@@ -19,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{50809508-F83
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam", "samples\gh-flow\src\Microsoft.AI.DevTeam\Microsoft.AI.DevTeam.csproj", "{79981945-61F7-4E1A-8949-7808FD75471B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam.Dapr", "samples\gh-flow\src\Microsoft.AI.DevTeam.Dapr\Microsoft.AI.DevTeam.Dapr.csproj", "{A7677950-18F1-42FF-8018-870395417465}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "seed-memory", "samples\gh-flow\src\seed-memory\seed-memory.csproj", "{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E6A25E68-EA75-4294-9B45-3CF2BB3B4ACD}"
@@ -66,10 +62,6 @@ Global
{B0BF1CD6-34E3-4ED4-9B2A-9B8781E9BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0BF1CD6-34E3-4ED4-9B2A-9B8781E9BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0BF1CD6-34E3-4ED4-9B2A-9B8781E9BE20}.Release|Any CPU.Build.0 = Release|Any CPU
{1972846E-4C21-4E40-B448-D78B73806BD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1972846E-4C21-4E40-B448-D78B73806BD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1972846E-4C21-4E40-B448-D78B73806BD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1972846E-4C21-4E40-B448-D78B73806BD9}.Release|Any CPU.Build.0 = Release|Any CPU
{A4AE4656-4919-45E2-9680-C317FBCF7693}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4AE4656-4919-45E2-9680-C317FBCF7693}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4AE4656-4919-45E2-9680-C317FBCF7693}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -78,10 +70,6 @@ Global
{79981945-61F7-4E1A-8949-7808FD75471B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79981945-61F7-4E1A-8949-7808FD75471B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79981945-61F7-4E1A-8949-7808FD75471B}.Release|Any CPU.Build.0 = Release|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.Build.0 = Release|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -140,12 +128,10 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B0BF1CD6-34E3-4ED4-9B2A-9B8781E9BE20} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{1972846E-4C21-4E40-B448-D78B73806BD9} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{A4AE4656-4919-45E2-9680-C317FBCF7693} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{E0E93575-7187-4975-8D72-6F285CD01767} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{50809508-F830-4553-9C4E-C802E0A0F690} = {E0E93575-7187-4975-8D72-6F285CD01767}
{79981945-61F7-4E1A-8949-7808FD75471B} = {50809508-F830-4553-9C4E-C802E0A0F690}
{A7677950-18F1-42FF-8018-870395417465} = {50809508-F830-4553-9C4E-C802E0A0F690}
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
{B9188ADC-D322-4B38-B3D6-95338E89C34B} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}

View File

@@ -18,10 +18,6 @@
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.18.0" />
<PackageVersion Include="CloudNative.CloudEvents.SystemTextJson" Version="2.7.1" />
<PackageVersion Include="Dapr.Actors" Version="1.13.1" />
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.13.1" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.13.1" />
<PackageVersion Include="Dapr.Client" Version="1.13.1" />
<PackageVersion Include="Elsa" Version="3.1.3" />
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.1.3" />
<PackageVersion Include="Elsa.EntityFrameworkCore.Sqlite" Version="3.1.3" />

View File

@@ -1,12 +0,0 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agents-pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""

View File

@@ -1,14 +0,0 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agents-statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"

View File

@@ -1,28 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
// The architect has Org+Repo scope and is holding the knowledge of the high level architecture of the project
public class Architect : AiAgent<ArchitectState>, IDaprAgent
{
public Architect(ActorHost host, DaprClient client, ISemanticTextMemory memory, Kernel kernel)
: base(host, client, memory, kernel)
{
}
public override Task HandleEvent(Event item)
{
return Task.CompletedTask;
}
}
public class ArchitectState
{
public string? FilesTree { get; set; }
public string? HighLevelArchitecture { get; set; }
}

View File

@@ -1,66 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
namespace Microsoft.AI.DevTeam.Dapr;
public class AzureGenie : Agent, IDaprAgent
{
private readonly IManageAzure _azureService;
public AzureGenie(ActorHost host, DaprClient client, IManageAzure azureService) : base(host, client)
{
_azureService = azureService;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeCreated):
{
var context = item.ToGithubContext();
await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "readme", "md", "output", item.Data["readme"]);
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.ReadmeStored),
Subject = context.Subject,
Data = context.ToData()
});
}
break;
case nameof(GithubFlowEventType.CodeCreated):
{
var context = item.ToGithubContext();
await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "run", "sh", "output", item.Data["code"]);
await RunInSandbox(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber);
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.SandboxRunCreated),
Subject = context.Subject,
Data = context.ToData()
});
}
break;
default:
break;
}
}
public async Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output)
{
await _azureService.Store(org, repo, parentIssueNumber, issueNumber, filename, extension, dir, output);
}
public async Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber)
{
await _azureService.RunInSandbox(org, repo, parentIssueNumber, issueNumber);
}
}

View File

@@ -1,86 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
public class Dev : AiAgent<DeveloperState>, IDaprAgent
{
private readonly ILogger<Dev> _logger;
public Dev(ActorHost host, DaprClient client, Kernel kernel, ISemanticTextMemory memory, ILogger<Dev> logger)
: base(host, client, memory, kernel)
{
_logger = logger;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.CodeGenerationRequested):
{
var context = item.ToGithubContext();
var code = await GenerateCode(item.Data["input"]);
var data = context.ToData();
data["result"] = code;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.CodeGenerated),
Subject = context.Subject,
Data = data
});
}
break;
case nameof(GithubFlowEventType.CodeChainClosed):
{
var context = item.ToGithubContext();
var lastCode = state.History.Last().Message;
var data = context.ToData();
data["code"] = lastCode;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.CodeCreated),
Subject = context.Subject,
Data = data
});
}
break;
default:
break;
}
}
public async Task<string> GenerateCode(string ask)
{
try
{
// TODO: ask the architect for the high level architecture as well as the files structure of the project
var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
var enhancedContext = await AddKnowledge(instruction, "waf", context);
return await CallFunction(DeveloperSkills.Implement, enhancedContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating code");
return "";
}
}
}
public class DeveloperState
{
public string? Understanding { get; set; }
}
public class UnderstandingResult
{
public required string NewUnderstanding { get; set; }
public required string Explanation { get; set; }
}

View File

@@ -1,57 +0,0 @@
namespace Microsoft.AI.DevTeam.Dapr;
public static class DeveloperSkills
{
public const string Implement = """
You are a Developer for an application.
Please output the code required to accomplish the task assigned to you below and wrap it in a bash script that creates the files.
Do not use any IDE commands and do not build and run the code.
Make specific choices about implementation. Do not offer a range of options.
Use comments in the code to describe the intent. Do not include other text other than code and code comments.
Input: {{$input}}
{{$waf}}
""";
public const string Improve = """
You are a Developer for an application. Your job is to imrove the code that you are given in the input below.
Please output a new version of code that fixes any problems with this version.
If there is an error message in the input you should fix that error in the code.
Wrap the code output up in a bash script that creates the necessary files by overwriting any previous files.
Do not use any IDE commands and do not build and run the code.
Make specific choices about implementation. Do not offer a range of options.
Use comments in the code to describe the intent. Do not include other text other than code and code comments.
Input: {{$input}}
{{$waf}}
""";
public const string Explain = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
Extract the key features and capabilities of the code file below, with the intent to build an understanding of an entire code repository.
You can include references or documentation links in your explanation. Also where appropriate please output a list of keywords to describe the code or its capabilities.
Example:
Keywords: Azure, networking, security, authentication
===code===
{{$input}}
===end-code===
Only include the points in a bullet point format and DON'T add anything outside of the bulleted list.
Be short and concise.
If the code's purpose is not clear output an error:
Error: The model could not determine the purpose of the code.
""";
public const string ConsolidateUnderstanding = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
You are trying to build an understanding of the codebase from code files. This is the current understanding of the project:
===current-understanding===
{{$input}}
===end-current-understanding===
and this is the new information that surfaced
===new-understanding===
{{$newUnderstanding}}
===end-new-understanding===
Your job is to update your current understanding with the new information.
Only include the points in a bullet point format and DON'T add anything outside of the bulleted list.
Be short and concise.
""";
}

View File

@@ -1,52 +0,0 @@
namespace Microsoft.AI.DevTeam.Dapr;
public static class DevLeadSkills
{
public const string Plan = """
You are a Dev Lead for an application team, building the application described below.
Please break down the steps and modules required to develop the complete application, describe each step in detail.
Make prescriptive architecture, language, and frameowrk choices, do not provide a range of choices.
For each step or module then break down the steps or subtasks required to complete that step or module.
For each subtask write an LLM prompt that would be used to tell a model to write the coee that will accomplish that subtask. If the subtask involves taking action/running commands tell the model to write the script that will run those commands.
In each LLM prompt restrict the model from outputting other text that is not in the form of code or code comments.
Please output a JSON array data structure, in the precise schema shown below, with a list of steps and a description of each step, and the steps or subtasks that each requires, and the LLM prompts for each subtask.
Example:
{
"steps": [
{
"step": "1",
"description": "This is the first step",
"subtasks": [
{
"subtask": "Subtask 1",
"description": "This is the first subtask",
"prompt": "Write the code to do the first subtask"
},
{
"subtask": "Subtask 2",
"description": "This is the second subtask",
"prompt": "Write the code to do the second subtask"
}
]
}
]
}
Do not output any other text.
Do not wrap the JSON in any other text, output the JSON format described above, making sure it's a valid JSON.
Input: {{$input}}
{{$waf}}
""";
public const string Explain = """
You are a Dev Lead.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
example:
Keywords: Azure, networking, security, authentication
If the code's purpose is not clear output an error:
Error: The model could not determine the purpose of the code.
--
Input: {{$input}}
""";
}

View File

@@ -1,99 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
public class DeveloperLead : AiAgent<DeveloperLeadState>, IDaprAgent
{
private readonly ILogger<DeveloperLead> _logger;
public DeveloperLead(ActorHost host, DaprClient client, Kernel kernel, ISemanticTextMemory memory, ILogger<DeveloperLead> logger)
: base(host, client, memory, kernel)
{
_logger = logger;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.DevPlanRequested):
{
var context = item.ToGithubContext();
var plan = await CreatePlan(item.Data["input"]);
var data = context.ToData();
data["result"] = plan;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.DevPlanGenerated),
Subject = context.Subject,
Data = data
});
}
break;
case nameof(GithubFlowEventType.DevPlanChainClosed):
{
var context = item.ToGithubContext();
var latestPlan = state.History.Last().Message;
var data = context.ToData();
data["plan"] = latestPlan;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.DevPlanCreated),
Subject = context.Subject,
Data = data
});
}
break;
default:
break;
}
}
public async Task<string> CreatePlan(string ask)
{
try
{
// TODO: Ask the architect for the existing high level architecture
// as well as the file structure
var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
var enhancedContext = await AddKnowledge(instruction, "waf", context);
return await CallFunction(DevLeadSkills.Plan, enhancedContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating development plan");
return "";
}
}
}
public class DevLeadPlanResponse
{
public required List<StepDescription> Steps { get; set; }
}
public class StepDescription
{
public required string Description { get; set; }
public required string Step { get; set; }
public required List<SubtaskDescription> subtasks { get; set; }
}
public class SubtaskDescription
{
public required string Subtask { get; set; }
public required string Prompt { get; set; }
}
public class DeveloperLeadState
{
public string? Plan { get; set; }
}

View File

@@ -1,100 +0,0 @@
using System.Text.Json;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
namespace Microsoft.AI.DevTeam.Dapr;
public class Hubber : Agent, IDaprAgent
{
private readonly IManageGithub _ghService;
public Hubber(ActorHost host, DaprClient client, IManageGithub ghService) : base(host, client)
{
_ghService = ghService;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.NewAsk):
{
var context = item.ToGithubContext();
var pmIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "PM.Readme", context.IssueNumber);
var devLeadIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "DevLead.Plan", context.IssueNumber);
await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{pmIssue} - tracks PM.Readme");
await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{devLeadIssue} - tracks DevLead.Plan");
await CreateBranch(context.Org, context.Repo, $"sk-{context.IssueNumber}");
}
break;
case nameof(GithubFlowEventType.ReadmeGenerated):
case nameof(GithubFlowEventType.DevPlanGenerated):
case nameof(GithubFlowEventType.CodeGenerated):
{
var context = item.ToGithubContext();
var result = item.Data["result"];
var contents = string.IsNullOrEmpty(result) ? "Sorry, I got tired, can you try again please? " : result;
await PostComment(context.Org, context.Repo, context.IssueNumber, contents);
}
break;
case nameof(GithubFlowEventType.DevPlanCreated):
{
var context = item.ToGithubContext();
var plan = JsonSerializer.Deserialize<DevLeadPlanResponse>(item.Data["plan"]);
var prompts = plan!.Steps.SelectMany(s => s.subtasks.Select(st => st.Prompt));
foreach (var prompt in prompts)
{
var functionName = "Developer.Implement";
var issue = await CreateIssue(context.Org, context.Repo, prompt, functionName, context.ParentNumber ?? 0);
var commentBody = $" - #{issue} - tracks {functionName}";
await PostComment(context.Org, context.Repo, context.ParentNumber ?? 0 , commentBody);
}
}
break;
case nameof(GithubFlowEventType.ReadmeStored):
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
await CommitToBranch(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "output", branch);
await CreatePullRequest(context.Org, context.Repo, context.ParentNumber ?? 0, branch);
}
break;
case nameof(GithubFlowEventType.SandboxRunFinished):
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
await CommitToBranch(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "output", branch);
}
break;
default:
break;
}
}
public async Task<int> CreateIssue(string org, string repo, string input, string function, long parentNumber)
{
return await _ghService.CreateIssue(org, repo, input, function, parentNumber);
}
public async Task PostComment(string org, string repo, long issueNumber, string comment)
{
await _ghService.PostComment(org, repo, issueNumber, comment);
}
public async Task CreateBranch(string org, string repo, string branch)
{
await _ghService.CreateBranch(org, repo, branch);
}
public async Task CreatePullRequest(string org, string repo, long issueNumber, string branch)
{
await _ghService.CreatePR(org, repo, issueNumber, branch);
}
public async Task CommitToBranch(string org, string repo, long parentNumber, long issueNumber, string rootDir, string branch)
{
await _ghService.CommitToBranch(org, repo, parentNumber, issueNumber, rootDir, branch);
}
}

View File

@@ -1,34 +0,0 @@
namespace Microsoft.AI.DevTeam.Dapr;
public static class PMSkills
{
public const string BootstrapProject = """
Please write a bash script with the commands that would be required to generate applications as described in the following input.
You may add comments to the script and the generated output but do not add any other text except the bash script.
You may include commands to build the applications but do not run them.
Do not include any git commands.
Input: {{$input}}
{{$waf}}
""";
public const string Readme = """
You are a program manager on a software development team. You are working on an app described below.
Based on the input below, and any dialog or other context, please output a raw README.MD markdown file documenting the main features of the app and the architecture or code organization.
Do not describe how to create the application.
Write the README as if it were documenting the features and architecture of the application. You may include instructions for how to run the application.
Input: {{$input}}
{{$waf}}
""";
public const string Explain = """
You are a Product Manager.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
example:
Keywords: Azure, networking, security, authentication
If the code's purpose is not clear output an error:
Error: The model could not determine the purpose of the code.
--
Input: {{$input}}
""";
}

View File

@@ -1,82 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
public class ProductManager : AiAgent<ProductManagerState>, IDaprAgent
{
private readonly ILogger<ProductManager> _logger;
public ProductManager(ActorHost host, DaprClient client, Kernel kernel, ISemanticTextMemory memory, ILogger<ProductManager> logger)
: base(host, client, memory, kernel)
{
_logger = logger;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeRequested):
{
var context = item.ToGithubContext();
var readme = await CreateReadme(item.Data["input"]);
var data = context.ToData();
data["result"] = readme;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.ReadmeGenerated),
Subject = context.Subject,
Data = data
});
}
break;
case nameof(GithubFlowEventType.ReadmeChainClosed):
{
var context = item.ToGithubContext();
var lastReadme = state.History.Last().Message;
var data = context.ToData();
data["readme"] = lastReadme;
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.ReadmeCreated),
Subject = context.Subject,
Data = data
});
}
break;
default:
break;
}
}
public async Task<string> CreateReadme(string ask)
{
try
{
var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
var enhancedContext = await AddKnowledge(instruction, "waf", context);
return await CallFunction(PMSkills.Readme, enhancedContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating readme");
return "";
}
}
}
public class ProductManagerState
{
public string? Capabilities { get; set; }
}

View File

@@ -1,111 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
namespace Microsoft.AI.DevTeam.Dapr;
public class Sandbox : Agent, IDaprAgent, IRemindable
{
private const string ReminderName = "SandboxRunReminder";
public const string StateStore = "agents-statestore";
private readonly IManageAzure _azService;
public Sandbox(ActorHost host, DaprClient client, IManageAzure azService) : base(host, client)
{
_azService = azService;
}
public override async Task HandleEvent(Event item)
{
ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.SandboxRunCreated):
{
var context = item.ToGithubContext();
await ScheduleCommitSandboxRun(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber);
}
break;
default:
break;
}
}
public async Task ScheduleCommitSandboxRun(string org, string repo, long parentIssueNumber, long issueNumber)
{
await StoreState(org, repo, parentIssueNumber, issueNumber);
await this.RegisterReminderAsync(
ReminderName,
null,
TimeSpan.Zero,
TimeSpan.FromMinutes(1));
}
private async Task StoreState(string org, string repo, long parentIssueNumber, long issueNumber)
{
var state = new SandboxMetadata
{
Org = org,
Repo = repo,
IssueNumber = issueNumber,
ParentIssueNumber = parentIssueNumber,
IsCompleted = false
};
await StateManager.SetStateAsync(
StateStore,
state);
}
private async Task Cleanup()
{
var agentState = await StateManager.GetStateAsync<SandboxMetadata>(StateStore);
agentState.IsCompleted = true;
await UnregisterReminderAsync(ReminderName);
await StateManager.SetStateAsync(
StateStore,
agentState);
}
public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period)
{
var agentState = await StateManager.GetStateAsync<SandboxMetadata>(StateStore);
if (!agentState.IsCompleted)
{
var sandboxId = $"sk-sandbox-{agentState.Org}-{agentState.Repo}-{agentState.ParentIssueNumber}-{agentState.IssueNumber}";
if (await _azService.IsSandboxCompleted(sandboxId))
{
await _azService.DeleteSandbox(sandboxId);
var data = new Dictionary<string, string> {
{ "org", agentState.Org },
{ "repo", agentState.Repo },
{ "issueNumber", agentState.IssueNumber.ToString() },
{ "parentNumber", agentState.ParentIssueNumber.ToString() }
};
var subject = $"{agentState.Org}-{agentState.Repo}-{agentState.IssueNumber}";
await PublishEvent(new Event
{
Namespace = Consts.MainTopic,
Type = nameof(GithubFlowEventType.SandboxRunFinished),
Subject = subject,
Data = data
});
await Cleanup();
}
}
else
{
await Cleanup();
}
}
}
public class SandboxMetadata
{
public required string Org { get; set; }
public required string Repo { get; set; }
public long ParentIssueNumber { get; set; }
public long IssueNumber { get; set; }
public bool IsCompleted { get; set; }
}

View File

@@ -1,23 +0,0 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 5274
EXPOSE 11111
EXPOSE 30000
ENV ASPNETCORE_URLS=http://+:5274
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
ARG configuration=Release
COPY . .
RUN dotnet restore "src/apps/gh-flow/gh-flow.csproj"
WORKDIR "/src/apps/gh-flow"
RUN dotnet build "gh-flow.csproj" -c $configuration -o /app/build
FROM build AS publish
ARG configuration=Release
RUN dotnet publish "gh-flow.csproj" -c $configuration -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "gh-flow.dll"]

View File

@@ -1,70 +0,0 @@
using System.Globalization;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Microsoft.AI.Agents.Abstractions;
namespace Microsoft.AI.DevTeam.Dapr.Events;
public enum GithubFlowEventType
{
NewAsk,
ReadmeChainClosed,
CodeChainClosed,
CodeGenerationRequested,
DevPlanRequested,
ReadmeGenerated,
DevPlanGenerated,
CodeGenerated,
DevPlanChainClosed,
ReadmeRequested,
ReadmeStored,
SandboxRunFinished,
ReadmeCreated,
CodeCreated,
DevPlanCreated,
SandboxRunCreated
}
public static class EventExtensions
{
public static GithubContext ToGithubContext(this Event evt)
{
ArgumentNullException.ThrowIfNull(evt);
return new GithubContext
{
Org = evt.Data["org"],
Repo = evt.Data["repo"],
IssueNumber = long.Parse(evt.Data["issueNumber"]),
ParentNumber = string.IsNullOrEmpty(evt.Data["parentNumber"]) ? default : long.Parse(evt.Data["parentNumber"])
};
}
public static Dictionary<string, string> ToData(this GithubContext context)
{
ArgumentNullException.ThrowIfNull(context);
return new Dictionary<string, string> {
{ "org", context.Org },
{ "repo", context.Repo },
{ "issueNumber", $"{context.IssueNumber}" },
{ "parentNumber", context.ParentNumber?.ToString(CultureInfo.InvariantCulture) ?? ""}
};
}
}
public class GithubContext
{
public required string Org { get; set; }
public required string Repo { get; set; }
public long IssueNumber { get; set; }
public long? ParentNumber { get; set; }
public string Subject => $"{Org}-{Repo}-{IssueNumber}";
}
[DataContract]
public class EventEnvelope
{
[JsonPropertyName("data")]
public required Event Data { get; set; }
}

View File

@@ -1,43 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<UserSecretsId>c073c86e-8483-4956-942f-331fd09172d4</UserSecretsId>
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Octokit.Webhooks.AspNetCore" />
<PackageReference Include="Octokit" />
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Qdrant" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Microsoft.Extensions.Azure" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Azure.ResourceManager.ContainerInstance" />
<PackageReference Include="Azure.Storage.Files.Shares" />
<PackageReference Include="Azure.Data.Tables" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Dapr.Actors.AspNetCore" />
<PackageReference Include="Dapr.AspNetCore" />
<PackageReference Include="CloudNative.CloudEvents.SystemTextJson" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.AI.Agents.Dapr\Microsoft.AI.Agents.Dapr.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,20 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class AzureOptions
{
[Required]
public required string SubscriptionId { get; set; }
[Required]
public required string Location { get; set; }
[Required]
public required string ContainerInstancesResourceGroup { get; set; }
[Required]
public required string FilesShareName { get; set; }
[Required]
public required string FilesAccountName { get; set; }
[Required]
public required string FilesAccountKey { get; set; }
[Required]
public required string SandboxImage { get; set; }
}

View File

@@ -1,8 +0,0 @@
namespace Microsoft.AI.DevTeam.Dapr;
public static class Consts
{
public const string MainTopic = "DevPersonas";
public const string PubSub = "agents-pubsub";
public const string AppId = "dev-agents";
}

View File

@@ -1,14 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class GithubOptions
{
[Required]
public required string AppKey { get; set; }
[Required]
public int AppId { get; set; }
[Required]
public long InstallationId { get; set; }
[Required]
public required string WebhookSecret { get; set; }
}

View File

@@ -1,18 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class OpenAIOptions
{
[Required]
public required string ServiceType { get; set; }
[Required]
public required string ServiceId { get; set; }
[Required]
public required string DeploymentOrModelId { get; set; }
[Required]
public required string EmbeddingDeploymentOrModelId { get; set; }
[Required]
public required string Endpoint { get; set; }
[Required]
public required string ApiKey { get; set; }
}

View File

@@ -1,10 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class QdrantOptions
{
[Required]
public required string Endpoint { get; set; }
[Required]
public int VectorSize { get; set; }
}

View File

@@ -1,8 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class ServiceOptions
{
[Required]
public required Uri IngesterUrl { get; set; }
}

View File

@@ -1,197 +0,0 @@
using System.Text.Json;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel;
using Octokit.Webhooks;
using Octokit.Webhooks.AspNetCore;
using Azure.Identity;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Http.Resilience;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Dapr;
using Dapr.Actors.Client;
using Dapr.Actors;
using Microsoft.AI.DevTeam.Dapr;
using Microsoft.AI.DevTeam.Dapr.Events;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<WebhookEventProcessor, GithubWebHookProcessor>();
builder.Services.AddTransient(CreateKernel);
builder.Services.AddTransient(CreateMemory);
builder.Services.AddHttpClient();
builder.Services.AddSingleton(s =>
{
var ghOptions = s.GetRequiredService<IOptions<GithubOptions>>();
var logger = s.GetRequiredService<ILogger<GithubAuthService>>();
var ghService = new GithubAuthService(ghOptions, logger);
var client = ghService.GetGitHubClient();
return client;
});
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.UseCredential(new DefaultAzureCredential());
clientBuilder.AddArmClient(default);
});
builder.Services.AddDaprClient();
builder.Services.AddActors(
options =>
{
options.UseJsonSerialization = true;
options.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
options.ReentrancyConfig = new ActorReentrancyConfig
{
Enabled = true
};
options.Actors.RegisterActor<Dev>();
options.Actors.RegisterActor<DeveloperLead>();
options.Actors.RegisterActor<ProductManager>();
options.Actors.RegisterActor<AzureGenie>();
options.Actors.RegisterActor<Hubber>();
options.Actors.RegisterActor<Sandbox>();
});
builder.Services.AddSingleton<GithubAuthService>();
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddOptions<GithubOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(GithubOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<AzureOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(AzureOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<OpenAIOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(OpenAIOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<QdrantOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(QdrantOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<ServiceOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(ServiceOptions)).Bind(settings);
})
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddSingleton<IManageAzure, AzureService>();
builder.Services.AddSingleton<IManageGithub, GithubService>();
builder.Services.AddSingleton<IAnalyzeCode, CodeAnalyzer>();
builder.Services.Configure<JsonSerializerOptions>(options =>
{
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
var app = builder.Build();
app.UseRouting()
.UseEndpoints(endpoints =>
{
var ghOptions = app.Services.GetRequiredService<IOptions<GithubOptions>>().Value;
endpoints.MapGitHubWebhooks(secret: ghOptions.WebhookSecret);
endpoints.MapActorsHandlers();
endpoints.MapSubscribeHandler();
});
app.UseCloudEvents();
app.MapPost("/developers", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\")", 1)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Dev), nameof(Dev.HandleEvent), evt));
app.MapPost("/devleads", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.DevPlanRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanChainClosed)}\")", 2)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(DeveloperLead), nameof(DeveloperLead.HandleEvent), evt));
app.MapPost("/productmanagers", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.ReadmeRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeChainClosed)}\")", 3)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(ProductManager), nameof(ProductManager.HandleEvent), evt));
app.MapPost("/hubbers", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.NewAsk)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanCreated)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeStored)}\") || (event.type ==\"{nameof(GithubFlowEventType.SandboxRunFinished)}\")", 4)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Hubber), nameof(Hubber.HandleEvent), evt));
app.MapPost("/azuregenies", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.ReadmeCreated)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeCreated)}\")", 5)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(AzureGenie), nameof(AzureGenie.HandleEvent), evt));
app.MapPost("/sandboxes", [Topic(Consts.PubSub, Consts.MainTopic, $"(event.type ==\"{nameof(GithubFlowEventType.SandboxRunCreated)}\")", 6)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Sandbox), nameof(Sandbox.HandleEvent), evt));
app.Run();
static async Task HandleEvent(IActorProxyFactory proxyFactory, string type, string method, EventEnvelope evt)
{
var proxyOptions = new ActorProxyOptions
{
RequestTimeout = Timeout.InfiniteTimeSpan
};
var proxy = proxyFactory.Create(new ActorId(evt.Data.Subject), type, proxyOptions);
await proxy.InvokeMethodAsync(method, evt.Data);
}
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
{
var openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
var qdrantConfig = provider.GetRequiredService<IOptions<QdrantOptions>>().Value;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole()
.AddDebug();
});
var memoryBuilder = new MemoryBuilder();
return memoryBuilder.WithLoggerFactory(loggerFactory)
.WithQdrantMemoryStore(qdrantConfig.Endpoint, qdrantConfig.VectorSize)
.WithAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingDeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey)
.Build();
}
static Kernel CreateKernel(IServiceProvider provider)
{
var openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
var clientOptions = new OpenAIClientOptions();
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var openAIClient = new OpenAIClient(new Uri(openAiConfig.Endpoint), new AzureKeyCredential(openAiConfig.ApiKey), clientOptions);
var builder = Kernel.CreateBuilder();
builder.Services.AddLogging(c => c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
builder.Services.AddAzureOpenAIChatCompletion(openAiConfig.DeploymentOrModelId, openAIClient);
builder.Services.ConfigureHttpClientDefaults(c =>
{
c.AddStandardResilienceHandler().Configure(o =>
{
o.Retry.MaxRetryAttempts = 5;
o.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
});
});
return builder.Build();
}

View File

@@ -1,38 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:59668",
"sslPort": 44354
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5244",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7227;http://localhost:5244",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,163 +0,0 @@
using System.Text;
using Azure;
using Azure.Core;
using Azure.ResourceManager;
using Azure.ResourceManager.ContainerInstance;
using Azure.ResourceManager.ContainerInstance.Models;
using Azure.ResourceManager.Resources;
using Azure.Storage.Files.Shares;
using Microsoft.Extensions.Options;
namespace Microsoft.AI.DevTeam.Dapr;
public class AzureService : IManageAzure
{
private readonly AzureOptions _azSettings;
private readonly ILogger<AzureService> _logger;
private readonly ArmClient _client;
public AzureService(IOptions<AzureOptions> azOptions, ILogger<AzureService> logger, ArmClient client)
{
ArgumentNullException.ThrowIfNull(azOptions);
ArgumentNullException.ThrowIfNull(logger);
ArgumentNullException.ThrowIfNull(client);
_azSettings = azOptions.Value;
_logger = logger;
_client = client;
}
public async Task DeleteSandbox(string sandboxId)
{
try
{
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
var collection = resourceGroupResource.GetContainerGroups();
var containerGroup = await collection.GetAsync(sandboxId);
await containerGroup.Value.DeleteAsync(WaitUntil.Started);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting sandbox");
throw;
}
}
public async Task<bool> IsSandboxCompleted(string sandboxId)
{
try
{
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
var collection = resourceGroupResource.GetContainerGroups();
var containerGroup = await collection.GetAsync(sandboxId);
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");
throw;
}
}
public async Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber)
{
try
{
var runId = $"sk-sandbox-{org}-{repo}-{parentIssueNumber}-{issueNumber}";
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
var scriptPath = $"/azfiles/output/{org}-{repo}/{parentIssueNumber}/{issueNumber}/run.sh";
var collection = resourceGroupResource.GetContainerGroups();
var data = new ContainerGroupData(new AzureLocation(_azSettings.Location), new ContainerInstanceContainer[]
{
new ContainerInstanceContainer(runId,_azSettings.SandboxImage,new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5,1)))
{
Command = { "/bin/bash", $"{scriptPath}" },
VolumeMounts =
{
new ContainerVolumeMount("azfiles","/azfiles/")
{
IsReadOnly = false,
}
},
}}, ContainerInstanceOperatingSystemType.Linux)
{
Volumes =
{
new ContainerVolume("azfiles")
{
AzureFile = new ContainerInstanceAzureFileVolume(_azSettings.FilesShareName,_azSettings.FilesAccountName)
{
StorageAccountKey = _azSettings.FilesAccountKey
},
},
},
RestartPolicy = ContainerGroupRestartPolicy.Never,
Sku = ContainerGroupSku.Standard,
Priority = ContainerGroupPriority.Regular
};
await collection.CreateOrUpdateAsync(WaitUntil.Completed, runId, data);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error running sandbox");
throw;
}
}
public async Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output)
{
ArgumentNullException.ThrowIfNull(output);
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
var parentDirName = $"{dir}/{org}-{repo}";
var fileName = $"{filename}.{extension}";
var share = new ShareClient(connectionString, _azSettings.FilesShareName);
await share.CreateIfNotExistsAsync();
await share.GetDirectoryClient($"{dir}").CreateIfNotExistsAsync(); ;
var parentDir = share.GetDirectoryClient(parentDirName);
await parentDir.CreateIfNotExistsAsync();
var parentIssueDir = parentDir.GetSubdirectoryClient($"{parentIssueNumber}");
await parentIssueDir.CreateIfNotExistsAsync();
var directory = parentIssueDir.GetSubdirectoryClient($"{issueNumber}");
await directory.CreateIfNotExistsAsync();
var file = directory.GetFileClient(fileName);
// hack to enable script to save files in the same directory
var cwdHack = "#!/bin/bash\n cd $(dirname $0)";
var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack, StringComparison.InvariantCulture) : output;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
{
await file.CreateAsync(stream.Length);
await file.UploadRangeAsync(
new HttpRange(0, stream.Length),
stream);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error storing output");
throw;
}
}
}
public interface IManageAzure
{
Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output);
Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber);
Task<bool> IsSandboxCompleted(string sandboxId);
Task DeleteSandbox(string sandboxId);
}

View File

@@ -1,55 +0,0 @@
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace Microsoft.AI.DevTeam.Dapr;
public interface IAnalyzeCode
{
Task<IEnumerable<CodeAnalysisResult>> Analyze(string content);
}
public class CodeAnalyzer : IAnalyzeCode
{
private readonly ServiceOptions _serviceOptions;
private readonly HttpClient _httpClient;
private readonly ILogger<CodeAnalyzer> _logger;
public CodeAnalyzer(IOptions<ServiceOptions> serviceOptions, HttpClient httpClient, ILogger<CodeAnalyzer> logger)
{
ArgumentNullException.ThrowIfNull(serviceOptions);
ArgumentNullException.ThrowIfNull(httpClient);
ArgumentNullException.ThrowIfNull(logger);
_serviceOptions = serviceOptions.Value;
_httpClient = httpClient;
_logger = logger;
_httpClient.BaseAddress = _serviceOptions.IngesterUrl;
}
public async Task<IEnumerable<CodeAnalysisResult>> Analyze(string content)
{
try
{
var request = new CodeAnalysisRequest { Content = content };
var response = await _httpClient.PostAsJsonAsync("api/AnalyzeCode", request);
var stringResult = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<IEnumerable<CodeAnalysisResult>>(stringResult);
return result ?? [];
}
catch (Exception ex)
{
_logger.LogError(ex, "Error analyzing code");
return [];
}
}
}
public class CodeAnalysisRequest
{
public required string Content { get; set; }
}
public class CodeAnalysisResult
{
public required string Meaning { get; set; }
public required string CodeBlock { get; set; }
}

View File

@@ -1,70 +0,0 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Octokit;
namespace Microsoft.AI.DevTeam.Dapr;
public class GithubAuthService
{
private readonly GithubOptions _githubSettings;
private readonly ILogger<GithubAuthService> _logger;
public GithubAuthService(IOptions<GithubOptions> ghOptions, ILogger<GithubAuthService> logger)
{
ArgumentNullException.ThrowIfNull(ghOptions);
ArgumentNullException.ThrowIfNull(logger);
_githubSettings = ghOptions.Value;
_logger = logger;
}
public string GenerateJwtToken(string appId, string appKey, int minutes)
{
using var rsa = RSA.Create();
rsa.ImportFromPem(appKey);
var securityKey = new RsaSecurityKey(rsa);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
var now = DateTime.UtcNow;
var iat = new DateTimeOffset(now).ToUnixTimeSeconds();
var exp = new DateTimeOffset(now.AddMinutes(minutes)).ToUnixTimeSeconds();
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Iat, iat.ToString(), ClaimValueTypes.Integer64),
new Claim(JwtRegisteredClaimNames.Exp, exp.ToString(), ClaimValueTypes.Integer64)
};
var token = new JwtSecurityToken(
issuer: appId,
claims: claims,
expires: DateTime.Now.AddMinutes(10),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public GitHubClient GetGitHubClient()
{
try
{
var jwtToken = GenerateJwtToken(_githubSettings.AppId.ToString(), _githubSettings.AppKey, 10);
var appClient = new GitHubClient(new ProductHeaderValue("SK-DEV-APP"))
{
Credentials = new Credentials(jwtToken, AuthenticationType.Bearer)
};
var response = appClient.GitHubApps.CreateInstallationToken(_githubSettings.InstallationId).Result;
return new GitHubClient(new ProductHeaderValue($"SK-DEV-APP-Installation{_githubSettings.InstallationId}"))
{
Credentials = new Credentials(response.Token)
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting GitHub client");
throw;
}
}
}

View File

@@ -1,232 +0,0 @@
using System.Text;
using Azure.Storage.Files.Shares;
using Microsoft.Extensions.Options;
using Octokit;
using Octokit.Helpers;
namespace Microsoft.AI.DevTeam.Dapr;
public class GithubService : IManageGithub
{
private readonly GitHubClient _ghClient;
private readonly AzureOptions _azSettings;
private readonly ILogger<GithubService> _logger;
private readonly HttpClient _httpClient;
public GithubService(IOptions<AzureOptions> azOptions, GitHubClient ghClient, ILogger<GithubService> logger, HttpClient httpClient)
{
ArgumentNullException.ThrowIfNull(azOptions);
ArgumentNullException.ThrowIfNull(ghClient);
ArgumentNullException.ThrowIfNull(logger);
ArgumentNullException.ThrowIfNull(httpClient);
_ghClient = ghClient;
_azSettings = azOptions.Value;
_logger = logger;
_httpClient = httpClient;
}
public async Task CommitToBranch(string org, string repo, long parentNumber, long issueNumber, string rootDir, string branch)
{
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
var dirName = $"{rootDir}/{org}-{repo}/{parentNumber}/{issueNumber}";
var share = new ShareClient(connectionString, _azSettings.FilesShareName);
var directory = share.GetDirectoryClient(dirName);
var remaining = new Queue<ShareDirectoryClient>();
remaining.Enqueue(directory);
while (remaining.Count > 0)
{
var dir = remaining.Dequeue();
await foreach (var item in dir.GetFilesAndDirectoriesAsync())
{
if (!item.IsDirectory && item.Name != "run.sh") // we don't want the generated script in the PR
{
try
{
var file = dir.GetFileClient(item.Name);
var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "", StringComparison.OrdinalIgnoreCase)
.Replace($"{dirName}/", "", StringComparison.OrdinalIgnoreCase);
var fileStream = await file.OpenReadAsync();
using (var reader = new StreamReader(fileStream, Encoding.UTF8))
{
var value = await reader.ReadToEndAsync();
await _ghClient.Repository.Content.CreateFile(
org, repo, filePath,
new CreateFileRequest($"Commit message", value, branch)); // TODO: add more meaningfull commit message
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while uploading file '{FileName}'.", item.Name);
}
}
else if (item.IsDirectory)
{
remaining.Enqueue(dir.GetSubdirectoryClient(item.Name));
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error committing to branch");
throw;
}
}
public async Task CreateBranch(string org, string repo, string branch)
{
try
{
var ghRepo = await _ghClient.Repository.Get(org, repo);
if (ghRepo.Size == 0)
{
// Create a new file and commit it to the repository
var createChangeSet = await _ghClient.Repository.Content.CreateFile(
org,
repo,
"README.md",
new CreateFileRequest("Initial commit", "# Readme")
);
}
await _ghClient.Git.Reference.CreateBranch(org, repo, branch, ghRepo.DefaultBranch);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating branch");
throw;
}
}
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");
throw;
}
}
public async Task<int> CreateIssue(string org, string repo, string input, string function, long parentNumber)
{
try
{
var newIssue = new NewIssue($"{function} chain for #{parentNumber}")
{
Body = input,
};
newIssue.Labels.Add(function);
newIssue.Labels.Add($"Parent.{parentNumber}");
var issue = await _ghClient.Issue.Create(org, repo, newIssue);
return issue.Number;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating issue");
throw;
}
}
public async Task CreatePR(string org, string repo, long number, string branch)
{
try
{
var ghRepo = await _ghClient.Repository.Get(org, repo);
await _ghClient.PullRequest.Create(org, repo, new NewPullRequest($"New app #{number}", branch, ghRepo.DefaultBranch));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating PR");
throw;
}
}
public async Task PostComment(string org, string repo, long issueNumber, string comment)
{
try
{
await _ghClient.Issue.Comment.Create(org, repo, (int)issueNumber, comment);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error posting comment");
throw;
}
}
public async Task<IEnumerable<FileResponse>> GetFiles(string org, string repo, string branch, Func<RepositoryContent, bool> filter)
{
ArgumentNullException.ThrowIfNull(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");
throw;
}
}
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)
{
if (item.Type == ContentType.File && filter(item))
{
var content = await _httpClient.GetStringAsync(new Uri(item.DownloadUrl));
result.Add(new FileResponse
{
Name = item.Name,
Content = content
});
}
else if (item.Type == ContentType.Dir)
{
var subItems = await _ghClient.Repository.Content.GetAllContentsByRef(org, repo, item.Path, branch);
result.AddRange(await CollectFiles(org, repo, branch, subItems, filter));
}
}
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error collecting files");
throw;
}
}
}
public class FileResponse
{
public required string Name { get; set; }
public required string Content { get; set; }
}
public interface IManageGithub
{
Task<int> CreateIssue(string org, string repo, string input, string functionName, long parentNumber);
Task CreatePR(string org, string repo, long number, string branch);
Task CreateBranch(string org, string repo, string branch);
Task CommitToBranch(string org, string repo, long parentNumber, long issueNumber, string rootDir, string branch);
Task PostComment(string org, string repo, long issueNumber, string comment);
Task<IEnumerable<FileResponse>> GetFiles(string org, string repo, string branch, Func<RepositoryContent, bool> filter);
Task<string> GetMainLanguage(string org, string repo);
}

View File

@@ -1,177 +0,0 @@
using System.Globalization;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.DevTeam.Dapr.Events;
using Octokit.Webhooks;
using Octokit.Webhooks.Events;
using Octokit.Webhooks.Events.IssueComment;
using Octokit.Webhooks.Events.Issues;
using Octokit.Webhooks.Models;
namespace Microsoft.AI.DevTeam.Dapr;
public sealed class GithubWebHookProcessor : WebhookEventProcessor
{
private readonly DaprClient _daprClient;
private readonly ILogger<GithubWebHookProcessor> _logger;
public GithubWebHookProcessor(DaprClient daprClient, ILogger<GithubWebHookProcessor> logger)
{
_daprClient = daprClient;
_logger = logger;
}
protected override async Task ProcessIssuesWebhookAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action)
{
ArgumentNullException.ThrowIfNull(headers);
ArgumentNullException.ThrowIfNull(issuesEvent);
ArgumentNullException.ThrowIfNull(action);
try
{
_logger.LogInformation("Processing issue event");
var org = issuesEvent.Repository!.Owner.Login;
var repo = issuesEvent.Repository.Name;
var issueNumber = issuesEvent.Issue.Number;
var input = issuesEvent.Issue.Body;
// Assumes the label follows the following convention: Skill.Function example: PM.Readme
// Also, we've introduced the Parent label, that ties the sub-issue with the parent issue
var labels = issuesEvent.Issue.Labels
.Select(l => l.Name.Split('.'))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0], parts => parts[1]);
var skillName = labels.Keys.First(k => k != "Parent");
long? parentNumber = labels.TryGetValue("Parent", out var value) ? long.Parse(value, CultureInfo.InvariantCulture) : null;
var suffix = $"{org}-{repo}";
if (issuesEvent.Action == IssuesAction.Opened)
{
_logger.LogInformation("Processing HandleNewAsk");
await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], input!, org, repo);
}
else if (issuesEvent.Action == IssuesAction.Closed && issuesEvent.Issue.User.Type.Value == UserType.Bot)
{
_logger.LogInformation("Processing HandleClosingIssue");
await HandleClosingIssue(issueNumber, parentNumber, skillName, labels[skillName], org, repo);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Processing issue event");
throw;
}
}
protected override async Task ProcessIssueCommentWebhookAsync(
WebhookHeaders headers,
IssueCommentEvent issueCommentEvent,
IssueCommentAction action)
{
ArgumentNullException.ThrowIfNull(headers);
ArgumentNullException.ThrowIfNull(issueCommentEvent);
ArgumentNullException.ThrowIfNull(action);
try
{
_logger.LogInformation("Processing issue comment event");
var org = issueCommentEvent.Repository!.Owner.Login;
var repo = issueCommentEvent.Repository.Name;
var issueNumber = issueCommentEvent.Issue.Number;
var input = issueCommentEvent.Comment.Body;
// Assumes the label follows the following convention: Skill.Function example: PM.Readme
var labels = issueCommentEvent.Issue.Labels
.Select(l => l.Name.Split('.'))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0], parts => parts[1]);
var skillName = labels.Keys.First(k => k != "Parent");
long? parentNumber = labels.TryGetValue("Parent", out var value) ? long.Parse(value, CultureInfo.InvariantCulture) : null;
var suffix = $"{org}-{repo}";
// we only respond to non-bot comments
if (issueCommentEvent.Sender!.Type.Value != UserType.Bot)
{
await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], input, org, repo);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Processing issue comment event");
throw;
}
}
private async Task HandleClosingIssue(long issueNumber, long? parentNumber, string skillName, string functionName, string org, string repo)
{
var subject = $"{org}-{repo}-{issueNumber}";
var eventType = (skillName, functionName) switch
{
("PM", "Readme") => nameof(GithubFlowEventType.ReadmeChainClosed),
("DevLead", "Plan") => nameof(GithubFlowEventType.DevPlanChainClosed),
("Developer", "Implement") => nameof(GithubFlowEventType.CodeChainClosed),
_ => nameof(GithubFlowEventType.NewAsk)
};
var data = new Dictionary<string, string>
{
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
{ "parentNumber", parentNumber?.ToString(CultureInfo.InvariantCulture) ?? "" }
};
var evt = new Event
{
Namespace = subject,
Type = eventType,
Subject = subject,
Data = data
};
await PublishEvent(evt);
}
private async Task HandleNewAsk(long issueNumber, long? parentNumber, string skillName, string functionName, string input, string org, string repo)
{
try
{
_logger.LogInformation("Handling new ask");
var subject = $"{org}-{repo}-{issueNumber}";
var eventType = (skillName, functionName) switch
{
("Do", "It") => nameof(GithubFlowEventType.NewAsk),
("PM", "Readme") => nameof(GithubFlowEventType.ReadmeRequested),
("DevLead", "Plan") => nameof(GithubFlowEventType.DevPlanRequested),
("Developer", "Implement") => nameof(GithubFlowEventType.CodeGenerationRequested),
_ => nameof(GithubFlowEventType.NewAsk)
};
var data = new Dictionary<string, string>
{
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
{ "parentNumber", parentNumber ?.ToString(CultureInfo.InvariantCulture) ?? ""},
{ "input" , input}
};
var evt = new Event
{
Namespace = subject,
Type = eventType,
Subject = subject,
Data = data
};
await PublishEvent(evt);
}
catch (Exception ex)
{
_logger.LogError(ex, "Handling new ask");
throw;
}
}
private async Task PublishEvent(Event evt)
{
var metadata = new Dictionary<string, string>() {
{ "cloudevent.Type", evt.Type },
{ "cloudevent.Subject", evt.Subject ?? "" },
{ "cloudevent.id", Guid.NewGuid().ToString()}
};
await _daprClient.PublishEventAsync(Consts.PubSub, Consts.MainTopic, evt, metadata);
}
}

View File

@@ -1,45 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information",
"Orleans.Streams": "Information"
}
},
"ApplicationInsights": {
"ConnectionString": ""
},
"AllowedHosts": "*",
"SANDBOX_IMAGE" : "mcr.microsoft.com/dotnet/sdk:7.0",
"GithubOptions" : {
"AppKey": "",
"AppId": "",
"InstallationId": "",
"WebhookSecret": ""
},
"AzureOptions" : {
"SubscriptionId":"",
"Location":"",
"ContainerInstancesResourceGroup":"",
"FilesShareName":"",
"FilesAccountName":"",
"FilesAccountKey":"",
"SandboxImage" : "mcr.microsoft.com/dotnet/sdk:7.0",
"ManagedIdentity": ""
},
"OpenAIOptions" : {
"ServiceType":"AzureOpenAI",
"ServiceId":"gpt-4",
"DeploymentOrModelId":"gpt-4",
"EmbeddingDeploymentOrModelId":"text-embedding-ada-002",
"Endpoint":"",
"ApiKey":""
},
"QdrantOptions" : {
"Endpoint" : "http://qdrant:6333",
"VectorSize" : "1536"
},
"ServiceOptions" : {
"IngesterUrl" : "http://localhost:7071"
}
}

View File

@@ -1,45 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information",
"Orleans.Streams": "Information"
}
},
"ApplicationInsights": {
"ConnectionString": ""
},
"AllowedHosts": "*",
"SANDBOX_IMAGE" : "mcr.microsoft.com/dotnet/sdk:7.0",
"GithubOptions" : {
"AppKey": "",
"AppId": "",
"InstallationId": "",
"WebhookSecret": ""
},
"AzureOptions" : {
"SubscriptionId":"",
"Location":"",
"ContainerInstancesResourceGroup":"",
"FilesShareName":"",
"FilesAccountName":"",
"FilesAccountKey":"",
"SandboxImage" : "mcr.microsoft.com/dotnet/sdk:7.0",
"ManagedIdentity": ""
},
"OpenAIOptions" : {
"ServiceType":"AzureOpenAI",
"ServiceId":"gpt-4",
"DeploymentOrModelId":"gpt-4",
"EmbeddingDeploymentOrModelId":"text-embedding-ada-002",
"Endpoint":"",
"ApiKey":""
},
"QdrantOptions" : {
"Endpoint" : "http://qdrant:6333",
"VectorSize" : "1536"
},
"ServiceOptions" : {
"IngesterUrl" : "http://localhost:7071"
}
}

View File

@@ -1,28 +0,0 @@
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
namespace Microsoft.AI.Agents.Dapr;
public abstract class Agent : Actor, IAgent
{
private readonly DaprClient _daprClient;
protected Agent(ActorHost host, DaprClient daprClient) : base(host)
{
this._daprClient = daprClient;
}
public abstract Task HandleEvent(Event item);
public async Task PublishEvent(Event item)
{
var metadata = new Dictionary<string, string>()
{
["cloudevent.Type"] = item.Type,
["cloudevent.Subject"] = item.Subject,
["cloudevent.id"] = Guid.NewGuid().ToString()
};
await _daprClient.PublishEventAsync("default", item.Namespace, item, metadata).ConfigureAwait(false);
}
}

View File

@@ -1,83 +0,0 @@
using System.Globalization;
using System.Text;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.Agents.Dapr;
public abstract class AiAgent<T> : Agent, IAiAgent where T : class, new()
{
public string StateStore = "agents-statestore";
public AiAgent(ActorHost host, DaprClient client, ISemanticTextMemory memory, Kernel kernel)
: base(host, client)
{
_memory = memory;
_kernel = kernel;
}
private readonly ISemanticTextMemory _memory;
private readonly Kernel _kernel;
protected AgentState<T> state = default!;
protected override async Task OnActivateAsync()
{
state = await StateManager.GetOrAddStateAsync(StateStore, new AgentState<T>());
}
public void AddToHistory(string message, ChatUserType userType)
{
if (state.History == null)
{
state.History = new List<ChatHistoryItem>();
}
state.History.Add(new ChatHistoryItem
{
Message = message,
Order = state.History.Count + 1,
UserType = userType
});
}
public string AppendChatHistory(string ask)
{
AddToHistory(ask, ChatUserType.User);
return string.Join("\n", state.History.Select(message => $"{message.UserType}: {message.Message}"));
}
public virtual async Task<string> CallFunction(string template, KernelArguments arguments, OpenAIPromptExecutionSettings? settings = null)
{
var propmptSettings = settings ?? new OpenAIPromptExecutionSettings { MaxTokens = 18000, Temperature = 0.8, TopP = 1 };
var function = _kernel.CreateFunctionFromPrompt(template, propmptSettings);
var result = (await _kernel.InvokeAsync(function, arguments)).ToString();
AddToHistory(result, ChatUserType.Agent);
await StateManager.SetStateAsync(
StateStore,
state);
return result;
}
/// <summary>
/// Adds knowledge to the
/// </summary>
/// <param name="instruction">The instruction string that uses the value of !index! as a placeholder to inject the data. Example:"Consider the following architectural guidelines: {waf}" </param>
/// <param name="index">Knowledge index</param>
/// <param name="arguments">The sk arguments, "input" is the argument </param>
/// <returns></returns>
public async Task<KernelArguments> AddKnowledge(string instruction, string index, KernelArguments arguments)
{
var documents = _memory.SearchAsync(index, arguments["input"]?.ToString()!, 5);
var kbStringBuilder = new StringBuilder();
await foreach (var doc in documents)
{
kbStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{doc.Metadata.Text}");
}
arguments[index] = instruction.Replace($"!{index}!", $"{kbStringBuilder}");
return arguments;
}
}

View File

@@ -1,9 +0,0 @@
using Dapr.Actors;
using Microsoft.AI.Agents.Abstractions;
namespace Microsoft.AI.Agents.Dapr;
public interface IDaprAgent : IActor
{
Task HandleEvent(Event item);
}

View File

@@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapr.Client" />
<PackageReference Include="Dapr.Actors" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AI.Agents\Microsoft.AI.Agents.csproj" />
</ItemGroup>
</Project>