Skip to content

Commit

Permalink
Merge pull request #9 from microsoft/imrpoving-devs
Browse files Browse the repository at this point in the history
Imrpoving devs
  • Loading branch information
rysweet authored Jun 17, 2023
2 parents 521b30c + 542e563 commit 3ed78a0
Showing 1 changed file with 51 additions and 47 deletions.
98 changes: 51 additions & 47 deletions cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,69 +13,43 @@ class Program
{
static async Task Main(string[] args)
{
var kernelSettings = KernelSettings.LoadSettings();
var kernelConfig = new KernelConfig();

using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning)
.AddConsole()
.AddDebug();
});

var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant", 1536, port: 6333));
var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey);
var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration);

var kernel = new KernelBuilder()
.WithLogger(loggerFactory.CreateLogger<IKernel>())
.WithAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey, true, kernelSettings.ServiceId, true)
.WithMemory(semanticTextMemory)
.WithConfiguration(kernelConfig)
.Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig
{
MaxRetryCount = 6,
UseExponentialBackoff = true,
// MinRetryDelay = TimeSpan.FromSeconds(2),
// MaxRetryDelay = TimeSpan.FromSeconds(8),
// MaxTotalRetryTime = TimeSpan.FromSeconds(30),
// RetryableStatusCodes = new[] { HttpStatusCode.TooManyRequests, HttpStatusCode.RequestTimeout },
// RetryableExceptions = new[] { typeof(HttpRequestException) }
}))
.Build();

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) => await ChainFunctions(file.FullName, kernel), fileOption);
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) => await CallWithFile<string>(nameof(PM), PM.Readme , file.FullName, kernel), fileOption);
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) => await CallWithFile<string>(nameof(PM), PM.BootstrapProject, file.FullName, kernel), fileOption);
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) => await CallWithFile<DevLeadPlanResponse>(nameof(DevLead), DevLead.Plan, file.FullName, kernel), fileOption);
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) => await CallWithFile<string>(nameof(Developer), Developer.Implement, file.FullName, kernel), fileOption);
devPlanCommand.SetHandler(async (file, maxRetry) => await CallWithFile<string>(nameof(Developer), Developer.Implement, file.FullName, maxRetry), fileOption, maxRetryOption);
devCommand.AddCommand(devPlanCommand);

rootCommand.AddCommand(pmCommand);
Expand All @@ -86,30 +60,30 @@ static async Task Main(string[] args)
await rootCommand.InvokeAsync(args);
}

public static async Task ChainFunctions(string file, IKernel kernel)
public static async Task ChainFunctions(string file, int maxRetry)
{
var sandboxSkill = new SandboxSkill();
var outputPath = Directory.CreateDirectory("output");

var readme = await CallWithFile<string>(nameof(PM), PM.Readme , file, kernel);
var readme = await CallWithFile<string>(nameof(PM), PM.Readme , file, maxRetry);
string readmeFile = Path.Combine(outputPath.FullName, "README.md");
await SaveToFile(readmeFile, readme);

var script = await CallWithFile<string>(nameof(PM), PM.BootstrapProject, file, kernel);
var script = await CallWithFile<string>(nameof(PM), PM.BootstrapProject, file, maxRetry);
await sandboxSkill.RunInDotnetAlpineAsync(script);
await SaveToFile(Path.Combine(outputPath.FullName, "bootstrap.sh"), script);

var plan = await CallWithFile<DevLeadPlanResponse>(nameof(DevLead), DevLead.Plan, readmeFile, kernel);
var plan = await CallWithFile<DevLeadPlanResponse>(nameof(DevLead), DevLead.Plan, readmeFile, maxRetry);
await SaveToFile(Path.Combine(outputPath.FullName, "plan.json"), JsonSerializer.Serialize(plan));

var implementationTasks = plan.steps.SelectMany(
(step) => step.subtasks.Select(
async (subtask) => {
var implementationResult = await CallFunction<string>(nameof(Developer), Developer.Implement, subtask.LLM_prompt, kernel);
var improvementResult = await CallFunction<string>(nameof(Developer), Developer.Improve, subtask.LLM_prompt, kernel);
var implementationResult = await CallFunction<string>(nameof(Developer), Developer.Implement, subtask.LLM_prompt, maxRetry);
//var improvementResult = await CallFunction<string>(nameof(Developer), Developer.Improve, subtask.LLM_prompt, maxRetry);
await sandboxSkill.RunInDotnetAlpineAsync(implementationResult);
await SaveToFile(Path.Combine(outputPath.FullName, $"{step.step}-{subtask.subtask}.sh"), improvementResult);
return improvementResult; }));
await SaveToFile(Path.Combine(outputPath.FullName, $"{step.step}-{subtask.subtask}.sh"), implementationResult);
return implementationResult; }));
await Task.WhenAll(implementationTasks);
}

Expand All @@ -118,16 +92,46 @@ 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, IKernel kernel)
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, kernel);
return await CallFunction<T>(skillName, functionName, input, maxRetry);
}

public static async Task<T> CallFunction<T>(string skillName, string functionName, string input, IKernel kernel)
public static async Task<T> CallFunction<T>(string skillName, string functionName, string input, int maxRetry)
{
var kernelSettings = KernelSettings.LoadSettings();
var kernelConfig = new KernelConfig();

using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning)
.AddConsole()
.AddDebug();
});
var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant", 1536, port: 6333));
var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey);
var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration);

var kernel = new KernelBuilder()
.WithLogger(loggerFactory.CreateLogger<IKernel>())
.WithAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey, true, kernelSettings.ServiceId, true)
.WithMemory(semanticTextMemory)
.WithConfiguration(kernelConfig)
.Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig
{
MaxRetryCount = maxRetry,
UseExponentialBackoff = true,
// MinRetryDelay = TimeSpan.FromSeconds(2),
// MaxRetryDelay = TimeSpan.FromSeconds(8),
MaxTotalRetryTime = TimeSpan.FromSeconds(300),
// RetryableStatusCodes = new[] { HttpStatusCode.TooManyRequests, HttpStatusCode.RequestTimeout },
// RetryableExceptions = new[] { typeof(HttpRequestException) }
}))
.Build();
Console.WriteLine($"Calling skill '{skillName}' function '{functionName}' with input '{input}'");
var interestingMemories = kernel.Memory.SearchAsync("waf-pages", input, 2);
var wafContext = "Consider the following architectural guidelines:";
Expand Down

0 comments on commit 3ed78a0

Please sign in to comment.