mirror of
https://github.com/microsoft/autogen.git
synced 2026-04-20 03:02:16 -04:00
cleanup
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
NewAsk
|
||||
-> create PM issue
|
||||
-> create DevLead issue
|
||||
-> create a branch
|
||||
```
|
||||
```
|
||||
ReadmeGenerated
|
||||
@@ -25,14 +26,23 @@
|
||||
CodeGenerated
|
||||
-> post comment
|
||||
```
|
||||
```
|
||||
ReadmeFinished
|
||||
-> commit to branch
|
||||
```
|
||||
```
|
||||
SandboxRunFinished
|
||||
-> commit to branch
|
||||
```
|
||||
|
||||
### AzureOps agent handles:
|
||||
```
|
||||
ReadmeFinished
|
||||
ReadmeChainClosed
|
||||
-> store
|
||||
-> ReadmeStored
|
||||
```
|
||||
```
|
||||
CodeFinished
|
||||
CodeChainClosed
|
||||
-> store
|
||||
-> run in sandbox
|
||||
```
|
||||
|
||||
@@ -7,12 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{088E1138-FF7
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cli", "src\apps\cli\cli.csproj", "{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gh-flow", "src\apps\gh-flow\gh-flow.csproj", "{37C45587-DD0C-4BFE-817A-21301567A94A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gh-flow-df", "src\apps\gh-flow-df\gh-flow-df.csproj", "{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "seed-memory", "src\apps\seed-memory\seed-memory.csproj", "{110B2C34-0F48-41CE-BC8F-AE3D24E79627}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowsApp", "src\apps\WorkflowsApp\WorkflowsApp.csproj", "{F288CF45-81EF-4F57-ABAC-D7ABE894E7A0}"
|
||||
@@ -26,24 +22,6 @@ EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AI.DevTeam.Skills", "src\libs\Microsoft.AI.DevTeam.Skills\Microsoft.AI.DevTeam.Skills.csproj", "{FF3D28B0-9344-44AF-BEEA-260187BE435E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {30612385-E4F4-4FD9-B648-45AF74CB4915}
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4D8667EC-09BE-4CB1-A278-E1CCD83B62B0} = {088E1138-FF7B-4179-AD37-4E3819C56D7E}
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{110B2C34-0F48-41CE-BC8F-AE3D24E79627} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{F288CF45-81EF-4F57-ABAC-D7ABE894E7A0} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{24EB6E3A-C080-4B27-8EA7-F6C91446B840} = {088E1138-FF7B-4179-AD37-4E3819C56D7E}
|
||||
{1F6EE104-0B3F-49C1-BE3B-B93CAD18BAD0} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
{9FA8DCFB-1726-42FB-B6CD-8AAC027810A1} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
{FF3D28B0-9344-44AF-BEEA-260187BE435E} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -53,18 +31,6 @@ Global
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1FE4A9AE-6403-48D6-8D06-A97CCCE608FA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -77,18 +43,6 @@ Global
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CF0F7B0D-3CC4-4068-A1A8-64E3151D01E3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{110B2C34-0F48-41CE-BC8F-AE3D24E79627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{110B2C34-0F48-41CE-BC8F-AE3D24E79627}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{110B2C34-0F48-41CE-BC8F-AE3D24E79627}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -150,4 +104,20 @@ Global
|
||||
{FF3D28B0-9344-44AF-BEEA-260187BE435E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FF3D28B0-9344-44AF-BEEA-260187BE435E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4D8667EC-09BE-4CB1-A278-E1CCD83B62B0} = {088E1138-FF7B-4179-AD37-4E3819C56D7E}
|
||||
{37C45587-DD0C-4BFE-817A-21301567A94A} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{110B2C34-0F48-41CE-BC8F-AE3D24E79627} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{F288CF45-81EF-4F57-ABAC-D7ABE894E7A0} = {4D8667EC-09BE-4CB1-A278-E1CCD83B62B0}
|
||||
{24EB6E3A-C080-4B27-8EA7-F6C91446B840} = {088E1138-FF7B-4179-AD37-4E3819C56D7E}
|
||||
{1F6EE104-0B3F-49C1-BE3B-B93CAD18BAD0} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
{9FA8DCFB-1726-42FB-B6CD-8AAC027810A1} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
{FF3D28B0-9344-44AF-BEEA-260187BE435E} = {24EB6E3A-C080-4B27-8EA7-F6C91446B840}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {30612385-E4F4-4FD9-B648-45AF74CB4915}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class Subtask
|
||||
{
|
||||
public string subtask { get; set; }
|
||||
public string prompt { get; set; }
|
||||
}
|
||||
|
||||
public class Step
|
||||
{
|
||||
public string description { get; set; }
|
||||
public string step { get; set; }
|
||||
public List<Subtask> subtasks { get; set; }
|
||||
}
|
||||
|
||||
public class DevLeadPlanResponse
|
||||
{
|
||||
public List<Step> steps { get; set; }
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
using System.CommandLine;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AI.DevTeam.Skills;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
|
||||
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
|
||||
using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Microsoft.SemanticKernel.Orchestration;
|
||||
using Microsoft.SemanticKernel.Plugins.Memory;
|
||||
|
||||
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
|
||||
var maxRetryOption = new Option<int>(
|
||||
name: "--maxRetry",
|
||||
description: "The number of retires to use if throttled",
|
||||
getDefaultValue: () => 6);
|
||||
|
||||
var fileOption = new Option<FileInfo?>(
|
||||
name: "--file",
|
||||
description: "The file used for input to the skill function");
|
||||
|
||||
var rootCommand = new RootCommand("CLI tool for the AI Dev team");
|
||||
rootCommand.AddGlobalOption(fileOption);
|
||||
rootCommand.Add(maxRetryOption);
|
||||
|
||||
var doCommand = new Command("do", "Doers :) ");
|
||||
var doItCommand = new Command("it", "Do it!");
|
||||
doItCommand.SetHandler(async (file, maxRetry) => await ChainFunctions(file.FullName, maxRetry), fileOption, maxRetryOption);
|
||||
doCommand.AddCommand(doItCommand);
|
||||
|
||||
var pmCommand = new Command("pm", "Commands for the PM team");
|
||||
var pmReadmeCommand = new Command("readme", "Produce a Readme for a given input");
|
||||
pmReadmeCommand.SetHandler(async (file, maxRetry) => await CallWithFile<string>(nameof(PM), PM.Readme, file.FullName, maxRetry), fileOption, maxRetryOption);
|
||||
|
||||
var pmBootstrapCommand = new Command("bootstrap", "Bootstrap a project for a given input");
|
||||
pmBootstrapCommand.SetHandler(async (file, maxRetry) => await CallWithFile<string>(nameof(PM), PM.BootstrapProject, file.FullName, maxRetry), fileOption, maxRetryOption);
|
||||
|
||||
pmCommand.AddCommand(pmReadmeCommand);
|
||||
pmCommand.AddCommand(pmBootstrapCommand);
|
||||
|
||||
var devleadCommand = new Command("devlead", "Commands for the Dev Lead team");
|
||||
var devleadPlanCommand = new Command("plan", "Plan the work for a given input");
|
||||
devleadPlanCommand.SetHandler(async (file, maxRetry) => await CallWithFile<DevLeadPlanResponse>(nameof(DevLead), DevLead.Plan, file.FullName, maxRetry), fileOption, maxRetryOption);
|
||||
devleadCommand.AddCommand(devleadPlanCommand);
|
||||
|
||||
var devCommand = new Command("dev", "Commands for the Dev team");
|
||||
var devPlanCommand = new Command("plan", "Implement the module for a given input");
|
||||
devPlanCommand.SetHandler(async (file, maxRetry) => await CallWithFile<string>(nameof(Developer), Developer.Implement, file.FullName, maxRetry), fileOption, maxRetryOption);
|
||||
devCommand.AddCommand(devPlanCommand);
|
||||
|
||||
rootCommand.AddCommand(pmCommand);
|
||||
rootCommand.AddCommand(devleadCommand);
|
||||
rootCommand.AddCommand(devCommand);
|
||||
rootCommand.AddCommand(doCommand);
|
||||
|
||||
await rootCommand.InvokeAsync(args);
|
||||
}
|
||||
|
||||
public static async Task ChainFunctions(string file, int maxRetry)
|
||||
{
|
||||
var sandboxSkill = new SandboxSkill();
|
||||
var outputPath = Directory.CreateDirectory("output");
|
||||
|
||||
Console.WriteLine($"Using output directory: {outputPath}");
|
||||
|
||||
var readme = await CallWithFile<string>(nameof(PM), PM.Readme, file, maxRetry);
|
||||
string readmeFile = Path.Combine(outputPath.FullName, "README.md");
|
||||
await SaveToFile(readmeFile, readme);
|
||||
Console.WriteLine($"Saved README to {readmeFile}");
|
||||
|
||||
var script = await CallWithFile<string>(nameof(PM), PM.BootstrapProject, file, maxRetry);
|
||||
await sandboxSkill.RunInDotnetAlpineAsync(script);
|
||||
await SaveToFile(Path.Combine(outputPath.FullName, "bootstrap.sh"), script);
|
||||
Console.WriteLine($"Saved bootstrap script to {outputPath.FullName}bootstrap.sh");
|
||||
|
||||
var plan = await CallWithFile<DevLeadPlanResponse>(nameof(DevLead), DevLead.Plan, readmeFile, maxRetry);
|
||||
await SaveToFile(Path.Combine(outputPath.FullName, "plan.json"), JsonSerializer.Serialize(plan));
|
||||
Console.WriteLine($"Using Plan: \n {plan}");
|
||||
|
||||
var implementationTasks = plan.steps.SelectMany(
|
||||
(step) => step.subtasks.Select(
|
||||
async (subtask) =>
|
||||
{
|
||||
Console.WriteLine($"Implementing {step.step}-{subtask.subtask}");
|
||||
var implementationResult = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
implementationResult = await CallFunction<string>(nameof(Developer), Developer.Implement, subtask.prompt, maxRetry);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.Message.Contains("TooMany"))
|
||||
{
|
||||
Console.WriteLine("Throttled, retrying...");
|
||||
continue;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
await sandboxSkill.RunInDotnetAlpineAsync(implementationResult);
|
||||
await SaveToFile(Path.Combine(outputPath.FullName, $"{step.step}-{subtask.subtask}.sh"), implementationResult);
|
||||
return implementationResult;
|
||||
}));
|
||||
await Task.WhenAll(implementationTasks);
|
||||
}
|
||||
|
||||
public static async Task SaveToFile(string filePath, string content)
|
||||
{
|
||||
await File.WriteAllTextAsync(filePath, content);
|
||||
}
|
||||
|
||||
public static async Task<T> CallWithFile<T>(string skillName, string functionName, string filePath, int maxRetry)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException($"File not found: {filePath}", filePath);
|
||||
var input = File.ReadAllText(filePath);
|
||||
return await CallFunction<T>(skillName, functionName, input, maxRetry);
|
||||
}
|
||||
|
||||
public static async Task<T> CallFunction<T>(string skillName, string functionName, string input, int maxRetry)
|
||||
{
|
||||
var kernelSettings = KernelSettings.LoadSettings();
|
||||
|
||||
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder
|
||||
.SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning)
|
||||
.AddConsole()
|
||||
.AddDebug();
|
||||
});
|
||||
var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant:6333", 1536));
|
||||
var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey);
|
||||
var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration);
|
||||
|
||||
|
||||
|
||||
var memoryBuilder = new MemoryBuilder();
|
||||
var memory = memoryBuilder.WithLoggerFactory(loggerFactory)
|
||||
.WithQdrantMemoryStore("http://qdrant:6333", 1536)
|
||||
.WithAzureTextEmbeddingGenerationService(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey)
|
||||
.Build();
|
||||
|
||||
var kernel = new KernelBuilder()
|
||||
.WithLoggerFactory(loggerFactory)
|
||||
.WithAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey, true, kernelSettings.ServiceId, true)
|
||||
.Build();
|
||||
|
||||
|
||||
|
||||
//Console.WriteLine($"Calling skill '{skillName}' function '{functionName}' with input '{input}'");
|
||||
var interestingMemories = memory.SearchAsync("waf-pages", input, 2);
|
||||
var wafContext = "Consider the following architectural guidelines:";
|
||||
await foreach (var m in interestingMemories)
|
||||
{
|
||||
wafContext += $"\n {m.Metadata.Text}";
|
||||
}
|
||||
var promptTemplate = Skills.ForSkillAndFunction(skillName, functionName);
|
||||
var function = kernel.CreateSemanticFunction(promptTemplate);
|
||||
|
||||
var context = new ContextVariables();
|
||||
context.Set("input", input);
|
||||
context.Set("wafContext", wafContext);
|
||||
|
||||
var answer = await kernel.RunAsync(context, function).ConfigureAwait(false);
|
||||
var result = typeof(T) != typeof(string) ? JsonSerializer.Deserialize<T>(answer.ToString()) : (T)(object)answer.ToString();
|
||||
//Console.WriteLine(answer);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PM { public static string Readme = "Readme"; public static string BootstrapProject = "BootstrapProject"; }
|
||||
public static class DevLead { public static string Plan = "Plan"; }
|
||||
public static class Developer { public static string Implement = "Implement"; public static string Improve = "Improve"; }
|
||||
@@ -1,37 +0,0 @@
|
||||
using DotNet.Testcontainers.Builders;
|
||||
|
||||
public class SandboxSkill
|
||||
{
|
||||
public async Task<string> RunInAlpineAsync(string input)
|
||||
{
|
||||
return await RunInContainer(input, "alpine");
|
||||
}
|
||||
|
||||
public async Task<string> RunInDotnetAlpineAsync(string input)
|
||||
{
|
||||
return await RunInContainer(input, "mcr.microsoft.com/dotnet/sdk:7.0");
|
||||
}
|
||||
|
||||
private async Task<string> RunInContainer(string input, string image)
|
||||
{
|
||||
var tempScriptFile = $"{Guid.NewGuid().ToString()}.sh";
|
||||
var tempScriptPath = $"./output/{tempScriptFile}";
|
||||
await File.WriteAllTextAsync(tempScriptPath, input);
|
||||
Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(),"output", "src"));
|
||||
var dotnetContainer = new ContainerBuilder()
|
||||
.WithName(Guid.NewGuid().ToString("D"))
|
||||
.WithImage(image)
|
||||
.WithBindMount(Path.Combine(Directory.GetCurrentDirectory(),"output", "src"), "/src")
|
||||
.WithBindMount(Path.Combine(Directory.GetCurrentDirectory(), tempScriptPath), $"/src/{tempScriptFile}")
|
||||
.WithWorkingDirectory("/src")
|
||||
.WithCommand("sh", tempScriptFile)
|
||||
.Build();
|
||||
|
||||
await dotnetContainer.StartAsync()
|
||||
.ConfigureAwait(false);
|
||||
// Cleanup
|
||||
File.Delete(tempScriptPath);
|
||||
File.Delete(Path.Combine(Directory.GetCurrentDirectory(), "output", "src", tempScriptFile));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.0.0-beta5" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Memory.Qdrant" Version="1.0.0-beta5" />
|
||||
<PackageReference Include="Testcontainers" Version="3.2.0" />
|
||||
<ProjectReference Include="..\..\libs\Microsoft.AI.DevTeam.Skills\Microsoft.AI.DevTeam.Skills.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,93 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
internal class KernelSettings
|
||||
{
|
||||
public const string DefaultConfigFile = "config/appsettings.json";
|
||||
public const string OpenAI = "OPENAI";
|
||||
public const string AzureOpenAI = "AZUREOPENAI";
|
||||
|
||||
[JsonPropertyName("serviceType")]
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("serviceId")]
|
||||
public string ServiceId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("deploymentOrModelId")]
|
||||
public string DeploymentOrModelId { get; set; } = string.Empty;
|
||||
[JsonPropertyName("embeddingDeploymentOrModelId")]
|
||||
public string EmbeddingDeploymentOrModelId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("endpoint")]
|
||||
public string Endpoint { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("apiKey")]
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("orgId")]
|
||||
public string OrgId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("logLevel")]
|
||||
public LogLevel? LogLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets.
|
||||
/// </summary>
|
||||
internal static KernelSettings LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(DefaultConfigFile))
|
||||
{
|
||||
return FromFile(DefaultConfigFile);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Semantic kernel settings '{DefaultConfigFile}' not found, attempting to load configuration from user secrets.");
|
||||
|
||||
return FromUserSecrets();
|
||||
}
|
||||
catch (InvalidDataException ide)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"Unable to load semantic kernel settings, please provide configuration settings using instructions in the README.\n" +
|
||||
"Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/sk-csharp-hello-world/README.md#configuring-the-starter"
|
||||
);
|
||||
throw new InvalidOperationException(ide.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the kernel settings from the specified configuration file if it exists.
|
||||
/// </summary>
|
||||
internal static KernelSettings FromFile(string configFile = DefaultConfigFile)
|
||||
{
|
||||
if (!File.Exists(configFile))
|
||||
{
|
||||
throw new FileNotFoundException($"Configuration not found: {configFile}");
|
||||
}
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(configFile, optional: true, reloadOnChange: true)
|
||||
.Build();
|
||||
|
||||
return configuration.Get<KernelSettings>()
|
||||
?? throw new InvalidDataException($"Invalid semantic kernel settings in '{configFile}', please provide configuration settings using instructions in the README.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the kernel settings from user secrets.
|
||||
/// </summary>
|
||||
internal static KernelSettings FromUserSecrets()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddUserSecrets<KernelSettings>()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
return configuration.Get<KernelSettings>()
|
||||
?? throw new InvalidDataException("Invalid semantic kernel settings in user secrets, please provide configuration settings using instructions in the README.");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
Please write a bash script with the commands that would be required to generate two new .NET applications as follows:
|
||||
The first application will be a web service called "skdtwebapi" with a two methods: /prompt, which accepts a PUT request with a text body of no more than 2048k, and /skills which accepts a GET request and lists the semantic skills it has.
|
||||
The second application will be a command line client of the web service and is called "skdt" and will accept one argument, a file name. The command line application will PUT the contents of the text file to the /prompt method of the first application.
|
||||
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.
|
||||
Use .NET 7.
|
||||
Configuration parameters required for the webapi applicaton will include AzureOpenAIServiceEndpoint, AIServiceKey, AIModel.
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create skdtwebapi web service
|
||||
dotnet new webapi -n skdtwebapi
|
||||
cd skdtwebapi
|
||||
|
||||
# Add required NuGet packages
|
||||
dotnet add package Microsoft.AspNetCore.OData
|
||||
dotnet add package Microsoft.Azure.CognitiveServices.Language
|
||||
|
||||
# Add configuration parameters to appsettings.json
|
||||
echo '{
|
||||
"AzureOpenAIServiceEndpoint": "",
|
||||
"AIServiceKey": "",
|
||||
"AIModel": ""
|
||||
}' > appsettings.json
|
||||
|
||||
# Add /prompt and /skills methods to ValuesController.cs
|
||||
echo 'using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Azure.CognitiveServices.Language.TextAnalytics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace skdtwebapi.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class ValuesController : ControllerBase
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public ValuesController(IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpGet("skills")]
|
||||
public async Task<ActionResult<IEnumerable<string>>> GetSkills()
|
||||
{
|
||||
var credentials = new ApiKeyServiceClientCredentials(_config["AIServiceKey"]);
|
||||
var client = new TextAnalyticsClient(credentials)
|
||||
{
|
||||
Endpoint = _config["AzureOpenAIServiceEndpoint"]
|
||||
};
|
||||
|
||||
var result = await client.EntitiesRecognitionGeneralAsync("en", "I am a software developer");
|
||||
|
||||
return result.Entities.Select(e => e.Name).ToList();
|
||||
}
|
||||
|
||||
[HttpPut("prompt")]
|
||||
public async Task<ActionResult> Prompt([FromBody] string prompt)
|
||||
{
|
||||
var credentials = new ApiKeyServiceClientCredentials(_config["AIServiceKey"]);
|
||||
var client = new TextAnalyticsClient(credentials)
|
||||
{
|
||||
Endpoint = _config["AzureOpenAIServiceEndpoint"]
|
||||
};
|
||||
|
||||
var result = await client.EntitiesRecognitionGeneralAsync("en", prompt);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}' > Controllers/ValuesController.cs
|
||||
|
||||
# Create skdt command line client
|
||||
cd ..
|
||||
dotnet new console -n skdt
|
||||
cd skdt
|
||||
|
||||
# Add required NuGet packages
|
||||
dotnet add package Microsoft.Extensions.Configuration
|
||||
dotnet add package Microsoft.Extensions.Configuration.Json
|
||||
dotnet add package Microsoft.Extensions.DependencyInjection
|
||||
dotnet add package Microsoft.Extensions.Http
|
||||
dotnet add package Microsoft.Net.Http.Headers
|
||||
|
||||
# Add configuration parameters to appsettings.json
|
||||
echo '{
|
||||
"WebApiUrl": "https://localhost:5001",
|
||||
"AIServiceKey": "",
|
||||
"AIModel": ""
|
||||
}' > appsettings.json
|
||||
|
||||
# Add code to Program.cs to PUT contents of text file to /prompt method
|
||||
echo 'using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Http;
|
||||
|
||||
namespace skdt
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddHttpClient()
|
||||
.BuildServiceProvider();
|
||||
|
||||
var clientFactory = serviceProvider.GetService<IHttpClientFactory>();
|
||||
var client = clientFactory.CreateClient();
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.Build();
|
||||
|
||||
var fileContents = await File.ReadAllTextAsync(args[0]);
|
||||
|
||||
var response = await client.PutAsync($"{configuration["WebApiUrl"]}/prompt", new StringContent(fileContents));
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine("Prompt sent successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Error sending prompt: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}' > Program.cs
|
||||
@@ -1,36 +0,0 @@
|
||||
Calling skill 'PM' function 'Readme' with file 'util/ToDoListSamplePrompt.txt'
|
||||
{"variables":[{"key":"input","value":"I\u0027d like to build a typical Todo List Application: a simple productivity tool that allows users to create, manage, and track tasks or to-do items. \nKey features of the Todo List application include the ability to add, edit, and delete tasks, set due dates and reminders, categorize tasks by project or priority, and mark tasks as complete. \nThe Todo List applications also offer collaboration features, such as sharing tasks with others or assigning tasks to team members.\nAdditionally, the Todo List application will offer offer mobile and web-based interfaces, allowing users to access their tasks from anywhere."}]}
|
||||
# Todo List Application
|
||||
|
||||
The Todo List Application is a simple productivity tool designed to help users create, manage, and track tasks or to-do items. With both mobile and web-based interfaces, users can access their tasks from anywhere, making it easy to stay organized and on top of their work.
|
||||
|
||||
## Features
|
||||
|
||||
- **Add, Edit, and Delete Tasks**: Users can easily create new tasks, modify existing ones, and remove tasks when they are no longer needed.
|
||||
- **Set Due Dates and Reminders**: Users can set due dates for tasks and receive reminders to ensure they stay on track and complete tasks on time.
|
||||
- **Categorize Tasks**: Users can organize tasks by project or priority, making it easy to focus on what's most important.
|
||||
- **Mark Tasks as Complete**: Users can mark tasks as complete, providing a sense of accomplishment and helping to track progress.
|
||||
- **Collaboration Features**: Users can share tasks with others or assign tasks to team members, making it easy to collaborate and work together on projects.
|
||||
- **Mobile and Web-Based Interfaces**: Users can access their tasks from anywhere, whether they're on their phone or using a web browser.
|
||||
|
||||
## Architecture
|
||||
|
||||
The Todo List Application is organized into the following components:
|
||||
|
||||
- **Frontend**: The frontend is responsible for displaying the user interface and handling user interactions. It is built using a combination of HTML, CSS, and JavaScript, and communicates with the backend through a RESTful API.
|
||||
- **Backend**: The backend is responsible for processing user requests, managing the database, and handling business logic. It is built using a server-side programming language (e.g., Node.js, Python, or Ruby) and a database management system (e.g., MySQL, PostgreSQL, or MongoDB).
|
||||
- **Database**: The database stores all the data related to tasks, projects, and users. It is organized into tables or collections, depending on the chosen database management system.
|
||||
- **API**: The API provides a set of endpoints for the frontend to interact with the backend. It follows the RESTful architecture, using standard HTTP methods (GET, POST, PUT, DELETE) to perform CRUD operations on tasks, projects, and users.
|
||||
- **Authentication and Authorization**: The application includes user authentication and authorization features, ensuring that only authorized users can access and modify their tasks and projects.
|
||||
|
||||
## Running the Application
|
||||
|
||||
To run the Todo List Application, follow these steps:
|
||||
|
||||
1. Clone the repository to your local machine.
|
||||
2. Install the required dependencies using the package manager for your chosen programming language (e.g., `npm install` for Node.js, `pip install -r requirements.txt` for Python, or `bundle install` for Ruby).
|
||||
3. Set up the database by following the instructions provided in the `database_setup.md` file.
|
||||
4. Start the backend server by running the appropriate command for your chosen programming language (e.g., `node server.js` for Node.js, `python manage.py runserver` for Python, or `rails server` for Ruby).
|
||||
5. Open the frontend in your web browser by navigating to the provided URL (e.g., `http://localhost:3000`).
|
||||
|
||||
For more detailed instructions, refer to the `installation.md` and `usage.md` files included in the repository.
|
||||
@@ -1,8 +0,0 @@
|
||||
I'd like to build a typical Chat Message Applicaton: a simple chat app in React Native that allows the user to manage multiple chat threads.
|
||||
Each thred is a different conversation. The app can look very similar to Apple iMessage.
|
||||
Key features of the Chat Message application include the ability to create a new chat thread, view the messages in each thread, and to edite and send a new message.
|
||||
The frontend for the app will be in typescript and React Native, the backend will be in C# as an Azure Function.
|
||||
The backend will use a mock function to generate random responses. The backend will store the chats in an Azure CosmosDB.
|
||||
The application will have a setup for local testing and development that allows testing without deployment to Azure.
|
||||
Use bicep scripts to create the Azure resources.
|
||||
The front end should be able to run entirely in the browser.
|
||||
@@ -1,6 +0,0 @@
|
||||
I'd like to build a typical Todo List Application: a simple productivity tool that allows users to create, manage, and track tasks or to-do items.
|
||||
Key features of the Todo List application include the ability to add, edit, and delete tasks, set due dates and reminders, categorize tasks by project or priority, and mark tasks as complete.
|
||||
The Todo List applications also offer collaboration features, such as sharing tasks with others or assigning tasks to team members.
|
||||
Additionally, the Todo List application will offer offer mobile and web-based interfaces, allowing users to access their tasks from anywhere.
|
||||
Use C# as the language.
|
||||
The app needs to be deployed to Azure, be highly performant, cost effective and secure, following the rules of Well Architected Framework.
|
||||
@@ -1,201 +0,0 @@
|
||||
{
|
||||
"skillName": "FunSkill",
|
||||
"functionName": "Excuses",
|
||||
"description": "Turn a scenario into a creative or humorous excuse to send your boss",
|
||||
"filePath": "skills/FunSkill/Excuses/skprompt.txt",
|
||||
"results": [
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "gpt-4",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "gpt-4",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "chat-completion",
|
||||
"chatSystemMessage": "You are an AI assistant that helps people find information."
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "bob"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:bob",
|
||||
"durationInMilliseconds": 3171.3077,
|
||||
"inputTokens": 46,
|
||||
"outputTokens": 24,
|
||||
"result": "Excuse: Bob was busy teaching a group of squirrels how to knit sweaters for the upcoming winter fashion show.",
|
||||
"chatCompletionResult": "Excuse: Bob was busy teaching a group of squirrels how to knit sweaters for the upcoming winter fashion show."
|
||||
}
|
||||
},
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "text-davinci-003",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "text-davinci-003",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "text-completion"
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "bob"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:bob",
|
||||
"durationInMilliseconds": 2507.7456,
|
||||
"inputTokens": 55,
|
||||
"outputTokens": 26,
|
||||
"result": " didn't show up to work\nExcuse:Bob was abducted by aliens and they needed him to help them fix their spaceship.",
|
||||
"textCompletionResult": " didn't show up to work\nExcuse:Bob was abducted by aliens and they needed him to help them fix their spaceship."
|
||||
}
|
||||
},
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "text-davinci-003",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "text-davinci-003",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "text-completion"
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "bob"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:bob",
|
||||
"durationInMilliseconds": 1427.7162,
|
||||
"inputTokens": 55,
|
||||
"outputTokens": 26,
|
||||
"result": " didn't show up to work\nExcuse:Bob was abducted by aliens and they needed him to help them fix their spaceship.",
|
||||
"textCompletionResult": " didn't show up to work\nExcuse:Bob was abducted by aliens and they needed him to help them fix their spaceship."
|
||||
}
|
||||
},
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "gpt-4",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "gpt-4",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "text-completion"
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "bob"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:bob",
|
||||
"durationInMilliseconds": 725.1863,
|
||||
"inputTokens": 46,
|
||||
"outputTokens": 1,
|
||||
"result": "bob",
|
||||
"textCompletionResult": "bob"
|
||||
}
|
||||
},
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "gpt-4",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "gpt-4",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "text-completion"
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "I'm in pain"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:I'm in pain",
|
||||
"durationInMilliseconds": 725.0451,
|
||||
"inputTokens": 48,
|
||||
"outputTokens": 4,
|
||||
"result": "I'm in pain",
|
||||
"textCompletionResult": "I'm in pain"
|
||||
}
|
||||
},
|
||||
{
|
||||
"completionConfig": {
|
||||
"label": "gpt-4",
|
||||
"aiService": "AzureOpenAI",
|
||||
"deploymentOrModelId": "gpt-4",
|
||||
"endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/",
|
||||
"useCustomEndpoint": false,
|
||||
"endpointType": "text-completion"
|
||||
},
|
||||
"requestSettings": {
|
||||
"temperature": 0,
|
||||
"topP": 0,
|
||||
"presencePenalty": 0,
|
||||
"frequencyPenalty": 0,
|
||||
"maxTokens": 0,
|
||||
"stopSequences": []
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"key": "input",
|
||||
"value": "I'm in pain"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"prompt": "Generate a creative reason or excuse for the given event. Be creative and be funny. Let your imagination run wild.\n\nEvent:I am running late.\nExcuse:I was being held ransom by giraffe gangsters. \n\nEvent:I'm in pain",
|
||||
"durationInMilliseconds": 1296.1323,
|
||||
"inputTokens": 48,
|
||||
"outputTokens": 4,
|
||||
"result": "I'm in pain",
|
||||
"textCompletionResult": "I'm in pain"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
7
src/apps/gh-flow-df/.vscode/extensions.json
vendored
7
src/apps/gh-flow-df/.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-azuretools.vscode-azurefunctions",
|
||||
"ms-dotnettools.csharp",
|
||||
"ms-semantic-kernel.semantic-kernel"
|
||||
]
|
||||
}
|
||||
11
src/apps/gh-flow-df/.vscode/launch.json
vendored
11
src/apps/gh-flow-df/.vscode/launch.json
vendored
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to .NET Functions",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:azureFunctions.pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
src/apps/gh-flow-df/.vscode/settings.json
vendored
7
src/apps/gh-flow-df/.vscode/settings.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"azureFunctions.deploySubpath": "bin/Release/net6.0/publish",
|
||||
"azureFunctions.projectLanguage": "C#",
|
||||
"azureFunctions.projectRuntime": "~4",
|
||||
"debug.internalConsoleOptions": "neverOpen",
|
||||
"azureFunctions.preDeployTask": "publish (functions)"
|
||||
}
|
||||
105
src/apps/gh-flow-df/.vscode/tasks.json
vendored
105
src/apps/gh-flow-df/.vscode/tasks.json
vendored
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "clean (functions)",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"clean",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"type": "process",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build (functions)",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"type": "process",
|
||||
"dependsOn": "clean (functions)",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "clean release (functions)",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"clean",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"type": "process",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish (functions)",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"publish",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"type": "process",
|
||||
"dependsOn": "clean release (functions)",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"type": "func",
|
||||
"dependsOn": "build (functions)",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/bin/Debug/net7.0"
|
||||
},
|
||||
"command": "host start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": "$func-dotnet-watch"
|
||||
},
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/sk-csharp-azure-functions.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/sk-csharp-azure-functions.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/sk-csharp-azure-functions.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.DurableTask.Client;
|
||||
using Octokit;
|
||||
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public class IssuesActivities
|
||||
{
|
||||
private readonly GithubService _ghService;
|
||||
|
||||
public IssuesActivities(GithubService githubService)
|
||||
{
|
||||
_ghService = githubService;
|
||||
}
|
||||
|
||||
[Function(nameof(CreateIssue))]
|
||||
public async Task<NewIssueResponse> CreateIssue([ActivityTrigger] NewIssueRequest request, FunctionContext executionContext)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var newIssue = new NewIssue($"{request.Function} chain for #{request.IssueRequest.Number}")
|
||||
{
|
||||
Body = request.IssueRequest.Input,
|
||||
|
||||
};
|
||||
newIssue.Labels.Add($"{request.Skill}.{request.Function}");
|
||||
var issue = await ghClient.Issue.Create(request.IssueRequest.Org, request.IssueRequest.Repo, newIssue);
|
||||
var commentBody = $" - [ ] #{issue.Number} - tracks {request.Skill}.{request.Function}";
|
||||
var comment = await ghClient.Issue.Comment.Create(request.IssueRequest.Org, request.IssueRequest.Repo, (int)request.IssueRequest.Number, commentBody);
|
||||
|
||||
return new NewIssueResponse
|
||||
{
|
||||
Number = issue.Number,
|
||||
CommentId = comment.Id
|
||||
};
|
||||
}
|
||||
|
||||
[Function("CloseSubOrchestration")]
|
||||
public async Task Close(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "close")] HttpRequestData req,
|
||||
[DurableClient] DurableTaskClient client)
|
||||
{
|
||||
var request = await req.ReadFromJsonAsync<CloseIssueRequest>();
|
||||
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
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);
|
||||
|
||||
await client.RaiseEventAsync(request.InstanceId, SubIssueOrchestration.IssueClosed, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Function(nameof(GetLastComment))]
|
||||
public async Task<string> GetLastComment([ActivityTrigger] IssueOrchestrationRequest request, FunctionContext executionContext)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var icOptions = new IssueCommentRequest
|
||||
{
|
||||
Direction = SortDirection.Descending
|
||||
};
|
||||
var apiOptions = new ApiOptions
|
||||
{
|
||||
PageCount = 1,
|
||||
PageSize = 1,
|
||||
StartPage = 1
|
||||
};
|
||||
|
||||
var comments = await ghClient.Issue.Comment.GetAllForIssue(request.Org, request.Repo, (int)request.Number, icOptions, apiOptions);
|
||||
return comments.First().Body;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using Azure.Data.Tables;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public static class MetadataActivities
|
||||
{
|
||||
[Function(nameof(GetMetadata))]
|
||||
public static async Task<IssueMetadata> GetMetadata(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "metadata/{key}")] HttpRequestData req, string key,
|
||||
[TableInput("Metadata", Connection = "AzureWebJobsStorage")] TableClient client,
|
||||
FunctionContext executionContext)
|
||||
{
|
||||
var logger = executionContext.GetLogger<SKWebHookEventProcessor>();
|
||||
|
||||
logger.LogInformation($"Getting metadata for {key}");
|
||||
|
||||
var metadataResponse = await client.GetEntityAsync<IssueMetadata>(key, key);
|
||||
var metadata = metadataResponse.Value;
|
||||
|
||||
logger.LogInformation($"Metadata result {JsonSerializer.Serialize(metadata)}");
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
[Function(nameof(SaveMetadata))]
|
||||
|
||||
public static async Task<IssueMetadata> SaveMetadata(
|
||||
[ActivityTrigger] IssueMetadata metadata,
|
||||
[TableInput("Metadata", Connection = "AzureWebJobsStorage")] TableClient client,
|
||||
FunctionContext executionContext)
|
||||
{
|
||||
await client.UpsertEntityAsync(metadata);
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
using System.Text;
|
||||
using Azure;
|
||||
using Azure.Core;
|
||||
using Azure.Data.Tables;
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.ContainerInstance;
|
||||
using Azure.ResourceManager.ContainerInstance.Models;
|
||||
using Azure.ResourceManager.Resources;
|
||||
using Azure.Storage.Files.Shares;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.DurableTask.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Octokit;
|
||||
using Octokit.Helpers;
|
||||
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public class PullRequestActivities
|
||||
{
|
||||
private readonly AzureOptions _azSettings;
|
||||
private readonly GithubService _ghService;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<PullRequestActivities> logger;
|
||||
|
||||
public PullRequestActivities(IOptions<AzureOptions> azOptions, GithubService ghService, IHttpClientFactory httpClientFactory, ILogger<PullRequestActivities> logger)
|
||||
{
|
||||
_azSettings = azOptions.Value;
|
||||
_ghService = ghService;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
[Function(nameof(SaveOutput))]
|
||||
public async Task<bool> SaveOutput([ActivityTrigger] SaveOutputRequest request, FunctionContext executionContext)
|
||||
{
|
||||
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
|
||||
var parentDirName = $"{request.Directory}/{request.IssueOrchestrationId}";
|
||||
var fileName = $"{request.FileName}.{request.Extension}";
|
||||
|
||||
var share = new ShareClient(connectionString, _azSettings.FilesShareName);
|
||||
await share.CreateIfNotExistsAsync();
|
||||
await share.GetDirectoryClient($"{request.Directory}").CreateIfNotExistsAsync();
|
||||
|
||||
var parentDir = share.GetDirectoryClient(parentDirName);
|
||||
await parentDir.CreateIfNotExistsAsync();
|
||||
|
||||
var directory = parentDir.GetSubdirectoryClient(request.SubOrchestrationId);
|
||||
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 output = request.Extension == "sh" ? request.Output.Replace("#!/bin/bash", cwdHack) : request.Output;
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(output)))
|
||||
{
|
||||
await file.CreateAsync(stream.Length);
|
||||
await file.UploadRangeAsync(
|
||||
new HttpRange(0, stream.Length),
|
||||
stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(CreateBranch))]
|
||||
public async Task<bool> CreateBranch([ActivityTrigger] GHNewBranch request, FunctionContext executionContext)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var repo = await ghClient.Repository.Get(request.Org, request.Repo);
|
||||
await ghClient.Git.Reference.CreateBranch(request.Org, request.Repo, request.Branch, repo.DefaultBranch);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(CreatePR))]
|
||||
public async Task<bool> CreatePR([ActivityTrigger] GHNewBranch request, FunctionContext executionContext)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var repo = await ghClient.Repository.Get(request.Org, request.Repo);
|
||||
await ghClient.PullRequest.Create(request.Org, request.Repo, new NewPullRequest($"New app #{request.Number}", request.Branch, repo.DefaultBranch));
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(RunInSandbox))]
|
||||
|
||||
public async Task<bool> RunInSandbox(
|
||||
[ActivityTrigger] RunInSandboxRequest request,
|
||||
[TableInput("ContainersMetadata", Connection = "AzureWebJobsStorage")] TableClient tableClient,
|
||||
FunctionContext executionContext)
|
||||
{
|
||||
var client = new ArmClient(new DefaultAzureCredential());
|
||||
|
||||
var containerGroupName = $"sk-sandbox-{request.PrRequest.SubOrchestrationId}";
|
||||
var containerName = $"sk-sandbox-{request.PrRequest.SubOrchestrationId}";
|
||||
var image = Environment.GetEnvironmentVariable("SANDBOX_IMAGE", EnvironmentVariableTarget.Process);
|
||||
|
||||
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
|
||||
var resourceGroupResource = client.GetResourceGroupResource(resourceGroupResourceId);
|
||||
|
||||
var scriptPath = $"/azfiles/output/{request.PrRequest.IssueOrchestrationId}/{request.PrRequest.SubOrchestrationId}/run.sh";
|
||||
|
||||
var collection = resourceGroupResource.GetContainerGroups();
|
||||
|
||||
var data = new ContainerGroupData(new AzureLocation(_azSettings.Location), new ContainerInstanceContainer[]
|
||||
{
|
||||
new ContainerInstanceContainer(containerName,image,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, containerGroupName, data);
|
||||
|
||||
var metadata = new ContainerInstanceMetadata
|
||||
{
|
||||
PartitionKey = containerGroupName,
|
||||
RowKey = containerGroupName,
|
||||
SubOrchestrationId = request.SanboxOrchestrationId,
|
||||
Processed = false
|
||||
};
|
||||
await tableClient.UpsertEntityAsync(metadata);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(CommitToGithub))]
|
||||
public async Task<bool> CommitToGithub([ActivityTrigger] GHCommitRequest request, FunctionContext executionContext)
|
||||
{
|
||||
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
|
||||
var dirName = $"{request.Directory}/{request.IssueOrchestrationId}/{request.SubOrchestrationId}";
|
||||
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}/", "")
|
||||
.Replace($"{dirName}/", "");
|
||||
var fileStream = await file.OpenReadAsync();
|
||||
using (var reader = new StreamReader(fileStream, Encoding.UTF8))
|
||||
{
|
||||
var value = reader.ReadToEnd();
|
||||
|
||||
await ghClient.Repository.Content.CreateFile(
|
||||
request.Org, request.Repo, filePath,
|
||||
new CreateFileRequest($"Commit message", value, request.Branch)); // TODO: add more meaningfull commit message
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, $"Error while uploading file {item.Name}");
|
||||
}
|
||||
}
|
||||
else if (item.IsDirectory)
|
||||
{
|
||||
remaining.Enqueue(dir.GetSubdirectoryClient(item.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(Terminated))]
|
||||
public async Task<ContainerInstanceMetadata> Terminated(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "container/{name}/terminate")] HttpRequestData req, string name,
|
||||
[TableInput("ContainersMetadata", Connection = "AzureWebJobsStorage")] TableClient tableClient,
|
||||
[DurableClient] DurableTaskClient client)
|
||||
{
|
||||
var metadataResponse = await tableClient.GetEntityAsync<ContainerInstanceMetadata>(name, name);
|
||||
var metadata = metadataResponse.Value;
|
||||
if (!metadata.Processed)
|
||||
{
|
||||
await client.RaiseEventAsync(metadata.SubOrchestrationId, SubIssueOrchestration.ContainerTerminated, true);
|
||||
metadata.Processed = true;
|
||||
await tableClient.UpdateEntityAsync(metadata, metadata.ETag, TableUpdateMode.Replace);
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
[Function(nameof(CleanContainers))]
|
||||
public async Task CleanContainers(
|
||||
[TimerTrigger("*/30 * * * * *")] TimerInfo myTimer,
|
||||
FunctionContext executionContext)
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient("FunctionsClient");
|
||||
var client = new ArmClient(new DefaultAzureCredential());
|
||||
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
|
||||
var resourceGroupResource = client.GetResourceGroupResource(resourceGroupResourceId);
|
||||
|
||||
var collection = resourceGroupResource.GetContainerGroups();
|
||||
|
||||
foreach (var cg in collection.GetAll())
|
||||
{
|
||||
var c = await cg.GetAsync();
|
||||
if (c.Value.Data.ProvisioningState == "Succeeded"
|
||||
&& c.Value.Data.Containers.First().InstanceView.CurrentState.State == "Terminated")
|
||||
{
|
||||
await cg.DeleteAsync(WaitUntil.Started);
|
||||
await httpClient.PostAsync($"container/{cg.Data.Name}/terminate", default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- Default properties inherited by all projects. Projects can override. -->
|
||||
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<AnalysisLevel>latest</AnalysisLevel>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<LangVersion>11</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<NoWarn>CS1591,CA1852,CA1050</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Disable NuGet packaging by default. Projects can override. -->
|
||||
<IsPackable>disable</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,25 +0,0 @@
|
||||
<Project>
|
||||
<!-- Direct all packages under 'dotnet' to get versions from Directory.Packages.props -->
|
||||
<!-- using Central Package Management feature -->
|
||||
<!-- https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
|
||||
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
|
||||
|
||||
<!-- Only run 'dotnet format' on dev machines, Release builds. Skip on GitHub Actions -->
|
||||
<!-- as this runs in its own Actions job. -->
|
||||
<Target Name="DotnetFormatOnBuild" BeforeTargets="Build"
|
||||
Condition=" '$(Configuration)' == 'Release' AND '$(GITHUB_ACTIONS)' == '' ">
|
||||
<Message Text="Running dotnet format" Importance="high" />
|
||||
<Exec Command="dotnet format --no-restore -v diag $(ProjectFileName)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="AddInternalsVisibleTo" BeforeTargets="BeforeCompile">
|
||||
<!-- Handle Add InternalsVisibleTo to any targets that don't support it. -->
|
||||
<ItemGroup Condition="'@(InternalsVisibleTo->Count())' > 0 AND $([MSBuild]::VersionLessThan($(NETCoreSdkVersion), '5.0.0'))">
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' != ''">%(InternalsVisibleTo.Identity), PublicKey="%(InternalsVisibleTo.PublicKey)</_Parameter1>
|
||||
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' == ''">%(InternalsVisibleTo.Identity)</_Parameter1>
|
||||
<_Parameter1_TypeName>System.String</_Parameter1_TypeName>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,10 +0,0 @@
|
||||
public class AddToPRRequest
|
||||
{
|
||||
public string Output { get; set; }
|
||||
public string IssueOrchestrationId { get; set; }
|
||||
public string SubOrchestrationId { get; set; }
|
||||
public string PrSubOrchestrationId { get; set; }
|
||||
public string Extension { get; set; }
|
||||
public bool RunInSandbox { get; set; }
|
||||
public IssueOrchestrationRequest Request { get; set; }
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
public class CloseIssueRequest
|
||||
{
|
||||
public string InstanceId { get; set; }
|
||||
public int CommentId { get; set; }
|
||||
public string Org { get; set; }
|
||||
public string Repo { get; set; }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Azure;
|
||||
using Azure.Data.Tables;
|
||||
|
||||
public class ContainerInstanceMetadata : ITableEntity
|
||||
{
|
||||
public string PartitionKey { get; set; }
|
||||
public string RowKey { get; set; }
|
||||
public string SubOrchestrationId { get; set; }
|
||||
public bool Processed { get; set; }
|
||||
public DateTimeOffset? Timestamp { get; set; }
|
||||
public ETag ETag { get; set; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
public class Subtask
|
||||
{
|
||||
public string subtask { get; set; }
|
||||
public string prompt { get; set; }
|
||||
}
|
||||
|
||||
public class Step
|
||||
{
|
||||
public string description { get; set; }
|
||||
public string step { get; set; }
|
||||
public List<Subtask> subtasks { get; set; }
|
||||
}
|
||||
|
||||
public class DevLeadPlanResponse
|
||||
{
|
||||
public List<Step> steps { get; set; }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
public class GHCommitRequest
|
||||
{
|
||||
public object IssueOrchestrationId { get; set; }
|
||||
public object SubOrchestrationId { get; set; }
|
||||
public string Org { get; set; }
|
||||
public string Repo { get; set; }
|
||||
public object Directory { get; set; }
|
||||
public string Branch { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
public class GHNewBranch
|
||||
{
|
||||
public string Org { get; set; }
|
||||
public string Repo { get; set; }
|
||||
public string Branch { get; set; }
|
||||
public object Number { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Azure;
|
||||
using Azure.Data.Tables;
|
||||
|
||||
public class IssueMetadata : ITableEntity
|
||||
{
|
||||
public long? Number { get; set; }
|
||||
public int? CommentId { get; set; }
|
||||
|
||||
public string? InstanceId { get; set; }
|
||||
|
||||
public string? Id { get; set; }
|
||||
|
||||
public string? Org { get; set; }
|
||||
public string? Repo { get; set; }
|
||||
public string PartitionKey { get; set; }
|
||||
public string RowKey { get; set; }
|
||||
public DateTimeOffset? Timestamp { get; set; }
|
||||
public ETag ETag { get; set; }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
public class IssueOrchestrationRequest
|
||||
{
|
||||
public string Org { get; set; }
|
||||
public string Repo { get; set; }
|
||||
public long Number { get; set; }
|
||||
public string Input { get; set; }
|
||||
public string Branch => $"sk-{Number}";
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
public class NewIssueRequest
|
||||
{
|
||||
public IssueOrchestrationRequest IssueRequest { get; set; }
|
||||
public string Skill { get; set; }
|
||||
public string Function { get; set; }
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
public class NewIssueResponse
|
||||
{
|
||||
public long Number { get; set; }
|
||||
public int CommentId { get; set; }
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
public class RunAndSaveRequest
|
||||
{
|
||||
public IssueOrchestrationRequest Request { get; set; }
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
public class RunInSandboxRequest
|
||||
{
|
||||
public AddToPRRequest PrRequest { get; set; }
|
||||
public string SanboxOrchestrationId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
public class SaveOutputRequest
|
||||
{
|
||||
public string IssueOrchestrationId { get; set; }
|
||||
public string SubOrchestrationId { get; set; }
|
||||
public string Output { get; set; }
|
||||
public string Extension { get; set; }
|
||||
public string Directory { get; set; }
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
public class SkillRequest
|
||||
{
|
||||
public IssueOrchestrationRequest IssueRequest { get; set; }
|
||||
public string Skill { get; set; }
|
||||
public string Function { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
public class SkillResponse<T>
|
||||
{
|
||||
public T Output { get; set; }
|
||||
public string SuborchestrationId { get; set; }
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.DurableTask;
|
||||
using Microsoft.DurableTask.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static SK.DevTeam.SubIssueOrchestration;
|
||||
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public static class IssueOrchestration
|
||||
{
|
||||
[Function("IssueOrchestrationStart")]
|
||||
public static async Task<string> HttpStart(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "doit")] HttpRequestData req,
|
||||
[DurableClient] DurableTaskClient client,
|
||||
FunctionContext executionContext)
|
||||
{
|
||||
ILogger logger = executionContext.GetLogger("IssueOrchestration_HttpStart");
|
||||
var request = await req.ReadFromJsonAsync<IssueOrchestrationRequest>();
|
||||
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
|
||||
nameof(IssueOrchestration), request);
|
||||
|
||||
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
|
||||
return "";
|
||||
}
|
||||
|
||||
[Function(nameof(IssueOrchestration))]
|
||||
public static async Task<List<string>> RunOrchestrator(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request)
|
||||
{
|
||||
var logger = context.CreateReplaySafeLogger(nameof(IssueOrchestration));
|
||||
var outputs = new List<string>();
|
||||
|
||||
var newGHBranchRequest = new GHNewBranch
|
||||
{
|
||||
Org = request.Org,
|
||||
Repo = request.Repo,
|
||||
Branch = request.Branch,
|
||||
Number = request.Number
|
||||
};
|
||||
|
||||
var newBranch = await context.CallActivityAsync<bool>(nameof(PullRequestActivities.CreateBranch), newGHBranchRequest);
|
||||
|
||||
var readmeTask = await context.CallSubOrchestratorAsync<bool>(nameof(ReadmeAndSave), new RunAndSaveRequest
|
||||
{
|
||||
Request = request,
|
||||
InstanceId = context.InstanceId
|
||||
});
|
||||
|
||||
var newPR = await context.CallActivityAsync<bool>(nameof(PullRequestActivities.CreatePR), newGHBranchRequest);
|
||||
|
||||
var planTask = await context.CallSubOrchestratorAsync<SkillResponse<string>>(nameof(CreatePlan), request);
|
||||
var plan = JsonSerializer.Deserialize<DevLeadPlanResponse>(planTask.Output);
|
||||
|
||||
var implementationTasks = plan.steps.SelectMany(s => s.subtasks.Select(st =>
|
||||
context.CallSubOrchestratorAsync<bool>(nameof(ImplementAndSave), new RunAndSaveRequest
|
||||
{
|
||||
Request = new IssueOrchestrationRequest
|
||||
{
|
||||
Number = request.Number,
|
||||
Org = request.Org,
|
||||
Repo = request.Repo,
|
||||
Input = st.prompt,
|
||||
},
|
||||
InstanceId = context.InstanceId
|
||||
})));
|
||||
|
||||
await Task.WhenAll(implementationTasks);
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
using Microsoft.AI.DevTeam.Skills;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.DurableTask;
|
||||
|
||||
namespace SK.DevTeam
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public static class SubIssueOrchestration
|
||||
{
|
||||
public static string IssueClosed = "IssueClosed";
|
||||
public static string ContainerTerminated = "ContainerTerminated";
|
||||
|
||||
private static async Task<SkillResponse<string>> CallSkill(TaskOrchestrationContext context, SkillRequest request)
|
||||
{
|
||||
var newIssueResponse = await context.CallActivityAsync<NewIssueResponse>(nameof(IssuesActivities.CreateIssue), new NewIssueRequest
|
||||
{
|
||||
IssueRequest = request.IssueRequest,
|
||||
Skill = request.Skill,
|
||||
Function = request.Function
|
||||
});
|
||||
|
||||
var metadata = await context.CallActivityAsync<IssueMetadata>(nameof(MetadataActivities.SaveMetadata), new IssueMetadata
|
||||
{
|
||||
Number = newIssueResponse.Number,
|
||||
InstanceId = context.InstanceId,
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
CommentId = newIssueResponse.CommentId,
|
||||
Org = request.IssueRequest.Org,
|
||||
Repo = request.IssueRequest.Repo,
|
||||
PartitionKey = $"{request.IssueRequest.Org}{request.IssueRequest.Repo}{newIssueResponse.Number}",
|
||||
RowKey = $"{request.IssueRequest.Org}{request.IssueRequest.Repo}{newIssueResponse.Number}",
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
});
|
||||
bool issueClosed = await context.WaitForExternalEvent<bool>(IssueClosed);
|
||||
var lastComment = await context.CallActivityAsync<string>(nameof(IssuesActivities.GetLastComment), new IssueOrchestrationRequest
|
||||
{
|
||||
Org = request.IssueRequest.Org,
|
||||
Repo = request.IssueRequest.Repo,
|
||||
Number = newIssueResponse.Number
|
||||
});
|
||||
|
||||
return new SkillResponse<string> { Output = lastComment, SuborchestrationId = context.InstanceId };
|
||||
}
|
||||
|
||||
[Function(nameof(CreateReadme))]
|
||||
public static async Task<SkillResponse<string>> CreateReadme(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request)
|
||||
{
|
||||
return await CallSkill(context, new SkillRequest
|
||||
{
|
||||
IssueRequest = request,
|
||||
Skill = nameof(PM),
|
||||
Function = nameof(PM.Readme)
|
||||
});
|
||||
}
|
||||
|
||||
[Function(nameof(CreatePlan))]
|
||||
public static async Task<SkillResponse<string>> CreatePlan(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request)
|
||||
{
|
||||
return await CallSkill(context, new SkillRequest
|
||||
{
|
||||
IssueRequest = request,
|
||||
Skill = nameof(DevLead),
|
||||
Function = nameof(DevLead.Plan)
|
||||
});
|
||||
}
|
||||
|
||||
[Function(nameof(Implement))]
|
||||
public static async Task<SkillResponse<string>> Implement(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request)
|
||||
{
|
||||
return await CallSkill(context, new SkillRequest
|
||||
{
|
||||
IssueRequest = request,
|
||||
Skill = nameof(Developer),
|
||||
Function = nameof(Developer.Implement)
|
||||
});
|
||||
}
|
||||
|
||||
[Function(nameof(ImplementAndSave))]
|
||||
public static async Task<bool> ImplementAndSave(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, RunAndSaveRequest request)
|
||||
{
|
||||
var implementResult = await context.CallSubOrchestratorAsync<SkillResponse<string>>(nameof(Implement), request.Request);
|
||||
await context.CallSubOrchestratorAsync<string>(nameof(AddToPR), new AddToPRRequest
|
||||
{
|
||||
Output = implementResult.Output,
|
||||
IssueOrchestrationId = request.InstanceId,
|
||||
SubOrchestrationId = implementResult.SuborchestrationId,
|
||||
Extension = "sh",
|
||||
RunInSandbox = true,
|
||||
Request = request.Request
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(ReadmeAndSave))]
|
||||
public static async Task<bool> ReadmeAndSave(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, RunAndSaveRequest request)
|
||||
{
|
||||
var readmeResult = await context.CallSubOrchestratorAsync<SkillResponse<string>>(nameof(CreateReadme), request.Request);
|
||||
context.CallSubOrchestratorAsync<string>(nameof(AddToPR), new AddToPRRequest
|
||||
{
|
||||
Output = readmeResult.Output,
|
||||
IssueOrchestrationId = request.InstanceId,
|
||||
SubOrchestrationId = readmeResult.SuborchestrationId,
|
||||
Extension = "md",
|
||||
RunInSandbox = false,
|
||||
Request = request.Request
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
[Function(nameof(AddToPR))]
|
||||
public static async Task<string> AddToPR(
|
||||
[OrchestrationTrigger] TaskOrchestrationContext context, AddToPRRequest request)
|
||||
{
|
||||
var saveScriptResponse = await context.CallActivityAsync<bool>(nameof(PullRequestActivities.SaveOutput), new SaveOutputRequest
|
||||
{
|
||||
Output = request.Output,
|
||||
IssueOrchestrationId = request.IssueOrchestrationId,
|
||||
SubOrchestrationId = request.SubOrchestrationId,
|
||||
Extension = request.Extension,
|
||||
Directory = "output",
|
||||
FileName = request.RunInSandbox ? "run" : "readme"
|
||||
});
|
||||
|
||||
if (request.RunInSandbox)
|
||||
{
|
||||
var newRequest = new RunInSandboxRequest
|
||||
{
|
||||
PrRequest = request,
|
||||
SanboxOrchestrationId = context.InstanceId
|
||||
};
|
||||
var runScriptResponse = await context.CallActivityAsync<bool>(nameof(PullRequestActivities.RunInSandbox), newRequest);
|
||||
bool containerTerminated = await context.WaitForExternalEvent<bool>(ContainerTerminated);
|
||||
}
|
||||
|
||||
// this is not ideal, as the script might be still running and there might be files that are not yet generated
|
||||
var commitResponse = await context.CallActivityAsync<bool>(nameof(PullRequestActivities.CommitToGithub), new GHCommitRequest
|
||||
{
|
||||
IssueOrchestrationId = request.IssueOrchestrationId,
|
||||
SubOrchestrationId = request.SubOrchestrationId,
|
||||
Directory = "output",
|
||||
Org = request.Request.Org,
|
||||
Repo = request.Request.Repo,
|
||||
Branch = request.Request.Branch
|
||||
});
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
|
||||
using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Octokit.Webhooks;
|
||||
using Octokit.Webhooks.AzureFunctions;
|
||||
|
||||
namespace KernelHttpServer;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
var host = new HostBuilder()
|
||||
.ConfigureFunctionsWorkerDefaults()
|
||||
.ConfigureGitHubWebhooks()
|
||||
.ConfigureAppConfiguration(configuration =>
|
||||
{
|
||||
var config = configuration.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
var builtConfig = config.Build();
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddTransient((provider) => CreateKernel(provider));
|
||||
services.AddScoped<GithubService>();
|
||||
services.AddScoped<WebhookEventProcessor, SKWebHookEventProcessor>();
|
||||
services.AddOptions<GithubOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection("GithubOptions").Bind(settings);
|
||||
});
|
||||
services.AddOptions<AzureOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection("AzureOptions").Bind(settings);
|
||||
});
|
||||
services.AddOptions<OpenAIOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection("OpenAIOptions").Bind(settings);
|
||||
});
|
||||
services.AddOptions<QdrantOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection("QdrantOptions").Bind(settings);
|
||||
});
|
||||
services.AddApplicationInsightsTelemetryWorkerService();
|
||||
services.ConfigureFunctionsApplicationInsights();
|
||||
// return JSON with expected lowercase naming
|
||||
services.Configure<JsonSerializerOptions>(options =>
|
||||
{
|
||||
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
});
|
||||
|
||||
|
||||
services.AddHttpClient("FunctionsClient", client =>
|
||||
{
|
||||
var fqdn = Environment.GetEnvironmentVariable("FUNCTIONS_FQDN", EnvironmentVariableTarget.Process);
|
||||
client.BaseAddress = new Uri($"{fqdn}/api/");
|
||||
});
|
||||
})
|
||||
.ConfigureLogging(logging =>
|
||||
{
|
||||
logging.Services.Configure<LoggerFilterOptions>(options =>
|
||||
{
|
||||
LoggerFilterRule defaultRule = options.Rules.FirstOrDefault(rule => rule.ProviderName
|
||||
== "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");
|
||||
if (defaultRule is not null)
|
||||
{
|
||||
options.Rules.Remove(defaultRule);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
private static IKernel CreateKernel(IServiceProvider provider)
|
||||
{
|
||||
var openAiConfig = provider.GetService<IOptions<OpenAIOptions>>().Value;
|
||||
var qdrantConfig = provider.GetService<IOptions<QdrantOptions>>().Value;
|
||||
|
||||
|
||||
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder
|
||||
.SetMinimumLevel(LogLevel.Debug)
|
||||
.AddConsole()
|
||||
.AddDebug();
|
||||
});
|
||||
|
||||
var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient(qdrantConfig.Endpoint, qdrantConfig.VectorSize));
|
||||
var embedingGeneration = new AzureTextEmbeddingGeneration(openAiConfig.EmbeddingDeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey);
|
||||
var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration);
|
||||
|
||||
return new KernelBuilder()
|
||||
.WithLoggerFactory(loggerFactory)
|
||||
.WithAzureChatCompletionService(openAiConfig.DeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey, true, openAiConfig.ServiceId, true)
|
||||
.WithMemory(semanticTextMemory)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
# Demo
|
||||
|
||||
## How do I get started?
|
||||
|
||||
Check - [Getting started](../docs/github-flow-getting-started.md)
|
||||
@@ -1,36 +0,0 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Octokit;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public class GithubService
|
||||
{
|
||||
private readonly GithubOptions _githubSettings;
|
||||
|
||||
public GithubService(IOptions<GithubOptions> ghOptions)
|
||||
{
|
||||
_githubSettings = ghOptions.Value;
|
||||
}
|
||||
public async Task<GitHubClient> GetGitHubClient()
|
||||
{
|
||||
// Use GitHubJwt library to create the GitHubApp Jwt Token using our private certificate PEM file
|
||||
var generator = new GitHubJwt.GitHubJwtFactory(
|
||||
new GitHubJwt.StringPrivateKeySource(_githubSettings.AppKey),
|
||||
new GitHubJwt.GitHubJwtFactoryOptions
|
||||
{
|
||||
AppIntegrationId = _githubSettings.AppId, // The GitHub App Id
|
||||
ExpirationSeconds = 600 // 10 minutes is the maximum time allowed
|
||||
}
|
||||
);
|
||||
|
||||
var jwtToken = generator.CreateEncodedJwtToken();
|
||||
var appClient = new GitHubClient(new ProductHeaderValue("SK-DEV-APP"))
|
||||
{
|
||||
Credentials = new Credentials(jwtToken, AuthenticationType.Bearer)
|
||||
};
|
||||
var response = await appClient.GitHubApps.CreateInstallationToken(_githubSettings.InstallationId);
|
||||
return new GitHubClient(new ProductHeaderValue($"SK-DEV-APP-Installation{_githubSettings.InstallationId}"))
|
||||
{
|
||||
Credentials = new Credentials(response.Token)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using Microsoft.AI.DevTeam.Skills;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
|
||||
using Microsoft.SemanticKernel.Orchestration;
|
||||
using Newtonsoft.Json;
|
||||
using Octokit.Webhooks;
|
||||
using Octokit.Webhooks.Events;
|
||||
using Octokit.Webhooks.Events.IssueComment;
|
||||
using Octokit.Webhooks.Events.Issues;
|
||||
using Octokit.Webhooks.Models;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")]
|
||||
public class SKWebHookEventProcessor : WebhookEventProcessor
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ILogger<SKWebHookEventProcessor> _logger;
|
||||
private readonly GithubService _ghService;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public SKWebHookEventProcessor(IKernel kernel, ILogger<SKWebHookEventProcessor> logger, GithubService ghService, IHttpClientFactory httpContextFactory)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_logger = logger;
|
||||
_ghService = ghService;
|
||||
_httpClientFactory = httpContextFactory;
|
||||
}
|
||||
protected override async Task ProcessIssuesWebhookAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var org = issuesEvent.Organization.Login;
|
||||
var repo = issuesEvent.Repository.Name;
|
||||
var issueNumber = issuesEvent.Issue.Number;
|
||||
var input = issuesEvent.Issue.Body;
|
||||
if (issuesEvent.Action == IssuesAction.Opened)
|
||||
{
|
||||
// Assumes the label follows the following convention: Skill.Function example: PM.Readme
|
||||
var labels = issuesEvent.Issue.Labels.First().Name.Split(".");
|
||||
var skillName = labels[0];
|
||||
var functionName = labels[1];
|
||||
if (skillName == "Do" && functionName == "It")
|
||||
{
|
||||
var issueOrchestrationRequest = new IssueOrchestrationRequest
|
||||
{
|
||||
Number = issueNumber,
|
||||
Org = org,
|
||||
Repo = repo,
|
||||
Input = input
|
||||
};
|
||||
var content = new StringContent(JsonConvert.SerializeObject(issueOrchestrationRequest), Encoding.UTF8, "application/json");
|
||||
var httpClient = _httpClientFactory.CreateClient("FunctionsClient");
|
||||
await httpClient.PostAsync("doit", content);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await RunSkill(skillName, functionName, input);
|
||||
await ghClient.Issue.Comment.Create(org, repo, (int)issueNumber, result);
|
||||
}
|
||||
}
|
||||
else if (issuesEvent.Action == IssuesAction.Closed && issuesEvent.Issue.User.Type.Value == UserType.Bot)
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient("FunctionsClient");
|
||||
var metadata = await httpClient.GetFromJsonAsync<IssueMetadata>($"metadata/{org}{repo}{issueNumber}");
|
||||
var closeIssueRequest = new CloseIssueRequest { InstanceId = metadata.InstanceId, CommentId = metadata.CommentId.Value, Org = org, Repo = repo };
|
||||
var content = new StringContent(JsonConvert.SerializeObject(closeIssueRequest), Encoding.UTF8, "application/json");
|
||||
_ = await httpClient.PostAsync("close", content);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task ProcessIssueCommentWebhookAsync(
|
||||
WebhookHeaders headers,
|
||||
IssueCommentEvent issueCommentEvent,
|
||||
IssueCommentAction action)
|
||||
{
|
||||
// we only resond to non-bot comments
|
||||
if (issueCommentEvent.Sender.Type.Value != UserType.Bot)
|
||||
{
|
||||
var ghClient = await _ghService.GetGitHubClient();
|
||||
var org = issueCommentEvent.Organization.Login;
|
||||
var repo = issueCommentEvent.Repository.Name;
|
||||
var issueId = issueCommentEvent.Issue.Number;
|
||||
|
||||
|
||||
// Assumes the label follows the following convention: Skill.Function example: PM.Readme
|
||||
var labels = issueCommentEvent.Issue.Labels.First().Name.Split(".");
|
||||
var skillName = labels[0];
|
||||
var functionName = labels[1];
|
||||
var input = issueCommentEvent.Comment.Body;
|
||||
var result = await RunSkill(skillName, functionName, input);
|
||||
|
||||
await ghClient.Issue.Comment.Create(org, repo, (int)issueId, result);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> RunSkill(string skillName, string functionName, string input)
|
||||
{
|
||||
var prompt = Skills.ForSkillAndFunction(skillName, functionName);
|
||||
var function = _kernel.CreateSemanticFunction(prompt, new OpenAIRequestSettings { MaxTokens = 8000, Temperature = 0.4, TopP = 1 });
|
||||
|
||||
var interestingMemories = _kernel.Memory.SearchAsync("waf-pages", input, 2);
|
||||
var wafContext = "Consider the following architectural guidelines:";
|
||||
await foreach (var memory in interestingMemories)
|
||||
{
|
||||
wafContext += $"\n {memory.Metadata.Text}";
|
||||
}
|
||||
|
||||
var context = new ContextVariables();
|
||||
context.Set("input", input);
|
||||
context.Set("wafContext", wafContext);
|
||||
|
||||
var result = await _kernel.RunAsync(context, function);
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
public class AzureOptions
|
||||
{
|
||||
public string SubscriptionId { get; set; }
|
||||
public string Location { get; set; }
|
||||
public string ContainerInstancesResourceGroup { get; set; }
|
||||
public string FilesShareName { get; set; }
|
||||
public string FilesAccountName { get; set; }
|
||||
public string FilesAccountKey { get; set; }
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
public class GithubOptions
|
||||
{
|
||||
public string AppKey { get; set; }
|
||||
public int AppId { get; set; }
|
||||
public long InstallationId { get; set; }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
public class OpenAIOptions
|
||||
{
|
||||
public string ServiceType { get; set; }
|
||||
public string ServiceId { get; set; }
|
||||
public string DeploymentOrModelId { get; set; }
|
||||
public string EmbeddingDeploymentOrModelId { get; set; }
|
||||
public string Endpoint { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
public class QdrantOptions
|
||||
{
|
||||
public string Endpoint { get; set; }
|
||||
public int VectorSize { get; set; }
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
|
||||
<UserSecretsId>b4703849-9b3a-41c3-b1bb-5ede533963c4</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace></RootNamespace>
|
||||
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
|
||||
<OutputType>Exe</OutputType>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.0.0-preview4" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.14.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.19.0" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
|
||||
<PackageReference Include="Octokit.Webhooks.AzureFunctions" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.0.0-beta5" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Memory.Qdrant" Version="1.0.0-beta5" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Tables" Version="1.2.0" />
|
||||
<PackageReference Include="Octokit" Version="7.1.0" />
|
||||
<PackageReference Include="GitHubJwt" Version="0.0.6" />
|
||||
<PackageReference Include="Azure.ResourceManager.ContainerInstance" Version="1.2.0-beta.1" />
|
||||
<PackageReference Include="Azure.Storage.Files.Shares" Version="12.16.0-beta.1" />
|
||||
<PackageReference Include="Azure.Data.Tables" Version="12.8.1" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.10.0-beta.1" />
|
||||
<ProjectReference Include="..\..\libs\Microsoft.AI.DevTeam.Skills\Microsoft.AI.DevTeam.Skills.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="host.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="local.settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Update="config\appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true,
|
||||
"excludedTypes": "Request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"durableTask": {
|
||||
"storageProvider": {
|
||||
"type": "AzureStorage"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"IsEncrypted": false,
|
||||
"Host": {
|
||||
"CORS": "*"
|
||||
},
|
||||
"Values": {
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
|
||||
"FUNCTIONS_FQDN": "http://localhost:7071",
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"AzureWebJobsFeatureFlags": "EnableHttpProxying",
|
||||
"SANDBOX_IMAGE" : "mcr.microsoft.com/dotnet/sdk:7.0",
|
||||
"GithubOptions:AppKey": "",
|
||||
"GithubOptions:AppId": "",
|
||||
"GithubOptions:InstallationId": "",
|
||||
"AzureOptions:SubscriptionId":"",
|
||||
"AzureOptions:Location":"",
|
||||
"AzureOptions:ContainerInstancesResourceGroup":"",
|
||||
"AzureOptions:FilesShareName":"",
|
||||
"AzureOptions:FilesAccountName":"",
|
||||
"AzureOptions:FilesAccountKey":"",
|
||||
"OpenAIOptions:ServiceType":"",
|
||||
"OpenAIOptions:ServiceId":"",
|
||||
"OpenAIOptions:DeploymentOrModelId":"",
|
||||
"OpenAIOptions:EmbeddingDeploymentOrModelId":"",
|
||||
"OpenAIOptions:Endpoint":"",
|
||||
"OpenAIOptions:ApiKey":"",
|
||||
"QdrantOptions:Endpoint":"http://qdrant:6333",
|
||||
"QdrantOptions:VectorSize":"1536"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"IsEncrypted": false,
|
||||
"Host": {
|
||||
"CORS": "*"
|
||||
},
|
||||
"Values": {
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
|
||||
// For local development, keep the default value
|
||||
// for Azure deployment, it will be injected as a variable in the bicep template
|
||||
"FUNCTIONS_FQDN": "localhost:7071",
|
||||
// For local development, keep the default value
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"AzureWebJobsFeatureFlags": "EnableHttpProxying",
|
||||
// This is the container image used as a base for the sandbox
|
||||
"SANDBOX_IMAGE" : "mcr.microsoft.com/devcontainers/universal:2-linux",
|
||||
// The private key generated for the Github App
|
||||
"GithubOptions__AppKey": "",
|
||||
// The App Id for the Github App
|
||||
"GithubOptions__AppId": "",
|
||||
// The instalation ID for the Github App (once installed to a repo or an org)
|
||||
"GithubOptions__InstallationId": "",
|
||||
// Azure subscription id
|
||||
"AzureOptions__SubscriptionId":"",
|
||||
// Location for the deployed resources in Azure
|
||||
"AzureOptions__Location":"",
|
||||
// Resource group in Azure, where ACI sandbox instances are going to be created
|
||||
"AzureOptions__ContainerInstancesResourceGroup":"",
|
||||
// Azure storage file share name (doesn't work with Azurite)
|
||||
"AzureOptions__FilesShareName":"",
|
||||
// Azure storage file share account name
|
||||
"AzureOptions__FilesAccountName":"",
|
||||
// Azure storage file share account key
|
||||
"AzureOptions__FilesAccountKey":"",
|
||||
// If using Azure - AzureOpenAI
|
||||
"OpenAIOptions__ServiceType":"",
|
||||
// the service id of the OpenAI model you want to use
|
||||
"OpenAIOptions__ServiceId":"",
|
||||
// the deployment id of the OpenAI model you want to use
|
||||
"OpenAIOptions__DeploymentOrModelId":"",
|
||||
// the embedding deployment id for the semantic memory
|
||||
"OpenAIOptions__EmbeddingDeploymentOrModelId":"",
|
||||
// the endpoint for the provisioned OpenAI service
|
||||
"OpenAIOptions__Endpoint":"",
|
||||
// the key for the provisioned OpenAI service
|
||||
"OpenAIOptions__ApiKey":"",
|
||||
// if using Codespaces, keep the default value
|
||||
"QdrantOptions__Endpoint":"http://qdrant:6333",
|
||||
// keep default
|
||||
"QdrantOptions__VectorSize":"1536"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,14 @@ using Orleans.Streams;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
public abstract class Agent : Grain, IGrainWithStringKey
|
||||
public abstract class AiAgent : Agent
|
||||
{
|
||||
public Agent(
|
||||
public AiAgent(
|
||||
[PersistentState("state", "messages")] IPersistentState<AgentState> state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
protected readonly IPersistentState<AgentState> _state;
|
||||
|
||||
public abstract Task HandleEvent(Event item, StreamSequenceToken? token);
|
||||
|
||||
protected async Task<ContextVariables> CreateWafContext(ISemanticTextMemory memory, string ask)
|
||||
{
|
||||
var context = new ContextVariables();
|
||||
@@ -53,6 +50,21 @@ public abstract class Agent : Grain, IGrainWithStringKey
|
||||
await _state.WriteStateAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract class Agent : Grain, IGrainWithStringKey
|
||||
{
|
||||
public abstract Task HandleEvent(Event item, StreamSequenceToken? token);
|
||||
protected async Task PublishEvent(string ns, string id, Event item)
|
||||
{
|
||||
var streamProvider = this.GetStreamProvider("StreamProvider");
|
||||
var streamId = StreamId.Create(ns, id);
|
||||
var stream = streamProvider.GetStream<Event>(streamId);
|
||||
await stream.OnNextAsync(item);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Microsoft.AI.DevTeam;
|
||||
|
||||
[StatelessWorker]
|
||||
[ImplicitStreamSubscription("AzureGenie")]
|
||||
public class AzureGenie : Grain, IGrainWithStringKey
|
||||
public class AzureGenie : Agent
|
||||
{
|
||||
private readonly IManageAzure _azureService;
|
||||
|
||||
@@ -24,12 +24,13 @@ public class AzureGenie : Grain, IGrainWithStringKey
|
||||
await stream.SubscribeAsync(HandleEvent);
|
||||
}
|
||||
|
||||
public async Task HandleEvent(Event item, StreamSequenceToken? token)
|
||||
public override async Task HandleEvent(Event item, StreamSequenceToken? token)
|
||||
{
|
||||
switch (item.Type)
|
||||
{
|
||||
case EventType.ReadmeChainClosed:
|
||||
//_azureService.Store();
|
||||
// postEvent ReadmeStored
|
||||
break;
|
||||
case EventType.CodeChainClosed:
|
||||
// _azureService.Store();
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
using Microsoft.AI.DevTeam.Skills;
|
||||
using Orleans.Runtime;
|
||||
using Orleans.Streams;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
public class Conductor : Grain
|
||||
{
|
||||
private readonly IManageGithub _ghService;
|
||||
|
||||
public Conductor(IManageGithub ghService)
|
||||
{
|
||||
_ghService = ghService;
|
||||
}
|
||||
|
||||
public async Task InitialFlow(string input, string org, string repo, long parentNumber)
|
||||
{
|
||||
// await _ghService.CreateBranch(org, repo, $"sk-{parentNumber}");
|
||||
|
||||
// var pmIssue = await _ghService.CreateIssue(org, repo, input, $"{nameof(PM)}.{nameof(PM.Readme)}", parentNumber);
|
||||
// var devLeadIssue = await _ghService.CreateIssue(org, repo, input, $"{nameof(DevLead)}.{nameof(DevLead.Plan)}", parentNumber);
|
||||
// var suffix = $"{org}-{repo}";
|
||||
// var lookup = GrainFactory.GetGrain<ILookupMetadata>(suffix);
|
||||
|
||||
// var metadataList = new List<StoreMetadataPairs>{
|
||||
// new StoreMetadataPairs
|
||||
// {
|
||||
// Key = pmIssue.IssueNumber,
|
||||
// Value = new NewIssueResponse { CommentId = pmIssue.CommentId, IssueNumber = (int)parentNumber}
|
||||
// },
|
||||
// new StoreMetadataPairs
|
||||
// {
|
||||
// Key = devLeadIssue.IssueNumber,
|
||||
// Value = new NewIssueResponse { CommentId = devLeadIssue.CommentId, IssueNumber = (int)parentNumber}
|
||||
// }
|
||||
// };
|
||||
// await lookup.StoreMetadata(metadataList);
|
||||
|
||||
// await githubActor.CreatePR(); // TODO: this should happen when all tasks are done?
|
||||
}
|
||||
public async Task ImplementationFlow(DevLeadPlanResponse plan, string org, string repo, int parentNumber)
|
||||
{
|
||||
// var suffix = $"{org}-{repo}";
|
||||
// var prompts = plan.steps.SelectMany(s => s.subtasks.Select(st => st.prompt));
|
||||
// var lookup = GrainFactory.GetGrain<ILookupMetadata>(suffix);
|
||||
// var metadataList = new List<StoreMetadataPairs>();
|
||||
// foreach (var prompt in prompts)
|
||||
// {
|
||||
// var issue = await _ghService.CreateIssue(org, repo, prompt, $"{nameof(Developer)}.{nameof(Developer.Implement)}", parentNumber);
|
||||
// metadataList.Add(new StoreMetadataPairs
|
||||
// {
|
||||
// Key = issue.IssueNumber,
|
||||
// Value = new NewIssueResponse { CommentId = issue.CommentId, IssueNumber = (int)parentNumber }
|
||||
// });
|
||||
// }
|
||||
// await lookup.StoreMetadata(metadataList);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Events:
|
||||
|
||||
NewAsk - Org, Repo, IssueNumber
|
||||
|
||||
-> PM subscribes -> check label,
|
||||
if Do It, create an issue, mark it as PM.Readme
|
||||
if PM.Readme send the prompt and create a comment
|
||||
-> DevLead subscribes -> check label,
|
||||
if Do It, create an issue, mark it as Devlead.Plan
|
||||
if DevLead.Plan, send the prompt and create a comment
|
||||
-> Developer subscribes -> check label,
|
||||
if Developer.Implement, send the prompt and create a comment
|
||||
|
||||
ChainClosed - Org, Repo, IssueNumber
|
||||
|
||||
-> PM subscribes -> check label,
|
||||
if PM.Readme send ReadmeCreated event
|
||||
-> DevLead subscribes -> check label,
|
||||
if DevLead.Plan, send PlanSubstepCreated event
|
||||
|
||||
-> Developer subscribes -> check label,
|
||||
if Developer.Implement, send ImplementSubstepCreated event
|
||||
|
||||
PlanSubstepCreated - Org, Repo, IssueNumber, Substep
|
||||
|
||||
-> Developer subscribes -> create an issue, mark it as Developer.Implement
|
||||
|
||||
|
||||
ReadmeCreated - store readme, commit to PR branch
|
||||
|
||||
ImplementSubstepCreated - store code, run in sandbox, commit to PR branch
|
||||
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.AI.DevTeam;
|
||||
|
||||
//[RegexImplicitStreamSubscription("")]
|
||||
[ImplicitStreamSubscription("developers")]
|
||||
public class Dev : Agent
|
||||
public class Dev : AiAgent
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ISemanticTextMemory _memory;
|
||||
@@ -50,6 +50,7 @@ public class Dev : Agent
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GenerateCode(string ask)
|
||||
{
|
||||
try
|
||||
@@ -62,7 +63,7 @@ public class Dev : Agent
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task<string> ReviewPlan(string plan)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Text.Json;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
[ImplicitStreamSubscription("DevPersonas")]
|
||||
public class DeveloperLead : Agent
|
||||
public class DeveloperLead : AiAgent
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ISemanticTextMemory _memory;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Microsoft.AI.DevTeam;
|
||||
|
||||
[StatelessWorker]
|
||||
[ImplicitStreamSubscription("Hubber")]
|
||||
public class Hubber : Grain, IGrainWithStringKey
|
||||
public class Hubber : Agent
|
||||
{
|
||||
private readonly IManageGithub _ghService;
|
||||
|
||||
@@ -25,7 +25,7 @@ public class Hubber : Grain, IGrainWithStringKey
|
||||
|
||||
await stream.SubscribeAsync(HandleEvent);
|
||||
}
|
||||
public async Task HandleEvent(Event item, StreamSequenceToken? token)
|
||||
public override async Task HandleEvent(Event item, StreamSequenceToken? token)
|
||||
{
|
||||
switch (item.Type)
|
||||
{
|
||||
@@ -47,6 +47,13 @@ public class Hubber : Grain, IGrainWithStringKey
|
||||
CreateIssue(item.Data["org"], item.Data["repo"], p, $"{nameof(Developer)}.{nameof(Developer.Implement)}", long.Parse(item.Data["parentNumber"])));
|
||||
Task.WaitAll(devTasks.ToArray());
|
||||
break;
|
||||
case EventType.ReadmeStored:
|
||||
|
||||
break;
|
||||
case EventType.SandboxRunFinished:
|
||||
// _azureService.Store();
|
||||
// _azureService.RunInSandbox();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -66,10 +73,10 @@ public class Hubber : Grain, IGrainWithStringKey
|
||||
}
|
||||
public async Task CreatePullRequest()
|
||||
{
|
||||
// await _ghService.CreatePR();
|
||||
//await _ghService.CreatePR();
|
||||
}
|
||||
public async Task CommitToBranch()
|
||||
{
|
||||
// await _ghService.CommitToBranch()
|
||||
//await _ghService.CommitToBranch()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using Orleans.Streams;
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
[ImplicitStreamSubscription("DevPersonas")]
|
||||
public class ProductManager : Agent
|
||||
public class ProductManager : AiAgent
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ISemanticTextMemory _memory;
|
||||
|
||||
@@ -22,5 +22,7 @@ public enum EventType
|
||||
DevPlanGenerated,
|
||||
CodeGenerated,
|
||||
DevPlanChainClosed,
|
||||
ReadmeRequested
|
||||
ReadmeRequested,
|
||||
ReadmeStored,
|
||||
SandboxRunFinished
|
||||
}
|
||||
Reference in New Issue
Block a user