diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..1ae6394b957 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +#### .NET Coding Conventions #### + +# this. and Me. preferences +dotnet_style_qualification_for_method = true + +#### Diagnostic configuration #### + +dotnet_diagnostic.SKEXP0001.severity = none +dotnet_diagnostic.SKEXP0020.severity = none +dotnet_diagnostic.SKEXP0010.severity = none \ No newline at end of file diff --git a/src/libs/Microsoft.AI.Agents/Abstractions/AiAgent.cs b/src/libs/Microsoft.AI.Agents/Abstractions/AiAgent.cs index fb03d45c98a..42401d9ac67 100644 --- a/src/libs/Microsoft.AI.Agents/Abstractions/AiAgent.cs +++ b/src/libs/Microsoft.AI.Agents/Abstractions/AiAgent.cs @@ -1,5 +1,7 @@ +using System.Text; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; +using Microsoft.SemanticKernel.Memory; using Orleans.Runtime; namespace Microsoft.AI.Agents.Abstractions; @@ -7,11 +9,15 @@ namespace Microsoft.AI.Agents.Abstractions; public abstract class AiAgent : Agent { public AiAgent( - [PersistentState("state", "messages")] IPersistentState> state) + [PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel) { _state = state; + _memory = memory; + _kernel = kernel; } protected IPersistentState> _state; + private readonly ISemanticTextMemory _memory; + private readonly Kernel _kernel; protected void AddToHistory(string message, ChatUserType userType) { @@ -30,19 +36,33 @@ protected string AppendChatHistory(string ask) return string.Join("\n", _state.State.History.Select(message => $"{message.UserType}: {message.Message}")); } - protected virtual async Task CallFunction(string template, KernelArguments arguments, Kernel kernel, OpenAIPromptExecutionSettings? settings = null) + protected virtual async Task CallFunction(string template, KernelArguments arguments, OpenAIPromptExecutionSettings? settings = null) { var propmptSettings = (settings == null) ? new OpenAIPromptExecutionSettings { MaxTokens = 18000, Temperature = 0.8, TopP = 1 } : settings; - var function = kernel.CreateFunctionFromPrompt(template, propmptSettings); - var result = (await kernel.InvokeAsync(function, arguments)).ToString(); + var function = _kernel.CreateFunctionFromPrompt(template, propmptSettings); + var result = (await _kernel.InvokeAsync(function, arguments)).ToString(); AddToHistory(result, ChatUserType.Agent); return result; } - protected async Task ShareContext() + /// + /// Adds knowledge to the + /// + /// The instruction string that uses the value of !index! as a placeholder to inject the data. Example:"Consider the following architectural guidelines: {waf}" + /// Knowledge index + /// The sk arguments, "input" is the argument + /// + protected async Task AddKnowledge(string instruction, string index, KernelArguments arguments) { - return _state.State.Data; + var documents = _memory.SearchAsync(index, arguments["input"].ToString(), 5); + var kbStringBuilder = new StringBuilder(); + await foreach (var doc in documents) + { + kbStringBuilder.AppendLine($"{doc.Metadata.Text}"); + } + arguments[index] = instruction.Replace($"!{index}!", $"{kbStringBuilder}"); + return arguments; } } diff --git a/src/libs/Microsoft.AI.Agents/Microsoft.AI.Agents.csproj b/src/libs/Microsoft.AI.Agents/Microsoft.AI.Agents.csproj index 26e6e5b6580..ad467acdfc3 100644 --- a/src/libs/Microsoft.AI.Agents/Microsoft.AI.Agents.csproj +++ b/src/libs/Microsoft.AI.Agents/Microsoft.AI.Agents.csproj @@ -8,7 +8,6 @@ - diff --git a/src/sample-apps/gh-flow/Agents/Architect/Architect.cs b/src/sample-apps/gh-flow/Agents/Architect/Architect.cs index aede613de9c..c345f436c20 100644 --- a/src/sample-apps/gh-flow/Agents/Architect/Architect.cs +++ b/src/sample-apps/gh-flow/Agents/Architect/Architect.cs @@ -1,5 +1,6 @@ using Microsoft.AI.Agents.Abstractions; -using Microsoft.KernelMemory; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Memory; using Orleans.Runtime; using Orleans.Streams; @@ -8,17 +9,16 @@ namespace Microsoft.AI.DevTeam; // The architect has Org+Repo scope and is holding the knowledge of the high level architecture of the project [ImplicitStreamSubscription(Consts.MainNamespace)] -public class Architect : AzureAiAgent +public class Architect : AiAgent { protected override string Namespace => Consts.MainNamespace; - public Architect([PersistentState("state", "messages")] IPersistentState> state, IKernelMemory memory) - : base(state, memory) + public Architect([PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel) + : base(state, memory, kernel) { } public override Task HandleEvent(Event item, StreamSequenceToken? token) { - // throw new NotImplementedException(); return Task.CompletedTask; } } diff --git a/src/sample-apps/gh-flow/Agents/AzureAiAgent.cs b/src/sample-apps/gh-flow/Agents/AzureAiAgent.cs deleted file mode 100644 index 4edb2fda2fa..00000000000 --- a/src/sample-apps/gh-flow/Agents/AzureAiAgent.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.KernelMemory; -using Microsoft.SemanticKernel; -using Orleans.Runtime; -using Microsoft.AI.Agents.Abstractions; -using Microsoft.SemanticKernel.Connectors.OpenAI; - -namespace Microsoft.AI.DevTeam; - -public abstract class AzureAiAgent : AiAgent -{ - private readonly IKernelMemory _memory; - - public AzureAiAgent([PersistentState("state", "messages")] IPersistentState> state, IKernelMemory memory) : base(state) - { - _memory = memory; - } - - protected async Task AddWafContext(IKernelMemory memory, KernelArguments arguments) - { - var waf = await memory.AskAsync(arguments["input"].ToString(), index: "waf"); - if (!waf.NoResult) arguments["wafContext"] = $"Consider the following architectural guidelines: ${waf.Result}"; - return arguments; - } - - protected override async Task CallFunction(string template, KernelArguments arguments, Kernel kernel, OpenAIPromptExecutionSettings? settings = null) - { - var wafArguments = await AddWafContext(_memory, arguments); - return await base.CallFunction(template, wafArguments, kernel, settings); - } -} \ No newline at end of file diff --git a/src/sample-apps/gh-flow/Agents/Developer/Developer.cs b/src/sample-apps/gh-flow/Agents/Developer/Developer.cs index 18983c89426..27988f69873 100644 --- a/src/sample-apps/gh-flow/Agents/Developer/Developer.cs +++ b/src/sample-apps/gh-flow/Agents/Developer/Developer.cs @@ -1,23 +1,22 @@ using Microsoft.AI.Agents.Abstractions; using Microsoft.AI.DevTeam.Events; -using Microsoft.KernelMemory; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Memory; using Orleans.Runtime; using Orleans.Streams; namespace Microsoft.AI.DevTeam; [ImplicitStreamSubscription(Consts.MainNamespace)] -public class Dev : AzureAiAgent, IDevelopApps +public class Dev : AiAgent, IDevelopApps { protected override string Namespace => Consts.MainNamespace; - private readonly Kernel _kernel; + private readonly ILogger _logger; - public Dev([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, IKernelMemory memory, ILogger logger) - : base(state, memory) + public Dev([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger) + : base(state, memory, kernel) { - _kernel = kernel; _logger = logger; } @@ -65,7 +64,9 @@ public async Task GenerateCode(string ask) { // TODO: ask the architect for the high level architecture as well as the files structure of the project var context = new KernelArguments { ["input"] = AppendChatHistory(ask)}; - return await CallFunction(DeveloperSkills.Implement, context, _kernel); + var instruction = "Consider the following architectural guidelines:!waf!"; + var enhancedContext = await AddKnowledge(instruction, "waf",context); + return await CallFunction(DeveloperSkills.Implement, enhancedContext); } catch (Exception ex) { @@ -73,40 +74,6 @@ public async Task GenerateCode(string ask) return default; } } - - // public async Task BuildUnderstanding(string content) - // { - // try - // { - // var explainFunction = _kernel.CreateSemanticFunction(Developer.Explain, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.8, TopP = 1 }); - // var consolidateFunction = _kernel.CreateSemanticFunction(Developer.ConsolidateUnderstanding, new OpenAIRequestSettings { MaxTokens = 15000, Temperature = 0.8, TopP = 1 }); - // var explainContext = new ContextVariables(); - // explainContext.Set("input", content); - // var explainResult = await _kernel.RunAsync(explainContext, explainFunction); - // var explainMesage = explainResult.ToString(); - - // var consolidateContext = new ContextVariables(); - // consolidateContext.Set("input", _state.State.Understanding); - // consolidateContext.Set("newUnderstanding", explainMesage); - - // var consolidateResult = await _kernel.RunAsync(consolidateContext, consolidateFunction); - // var consolidateMessage = consolidateResult.ToString(); - - // _state.State.Understanding = consolidateMessage; - // await _state.WriteStateAsync(); - - // return new UnderstandingResult - // { - // NewUnderstanding = consolidateMessage, - // Explanation = explainMesage - // }; - // } - // catch (Exception ex) - // { - // _logger.LogError(ex, "Error building understanding"); - // return default; - // } - // } } [GenerateSerializer] diff --git a/src/sample-apps/gh-flow/Agents/DeveloperLead/DeveloperLead.cs b/src/sample-apps/gh-flow/Agents/DeveloperLead/DeveloperLead.cs index 8e125ede8fc..e2ce5eb33f1 100644 --- a/src/sample-apps/gh-flow/Agents/DeveloperLead/DeveloperLead.cs +++ b/src/sample-apps/gh-flow/Agents/DeveloperLead/DeveloperLead.cs @@ -1,23 +1,20 @@ using Microsoft.AI.Agents.Abstractions; using Microsoft.AI.DevTeam.Events; -using Microsoft.KernelMemory; using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.OpenAI; +using Microsoft.SemanticKernel.Memory; using Orleans.Runtime; using Orleans.Streams; namespace Microsoft.AI.DevTeam; [ImplicitStreamSubscription(Consts.MainNamespace)] -public class DeveloperLead : AzureAiAgent, ILeadDevelopers +public class DeveloperLead : AiAgent, ILeadDevelopers { protected override string Namespace => Consts.MainNamespace; - private readonly Kernel _kernel; private readonly ILogger _logger; - public DeveloperLead([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, IKernelMemory memory, ILogger logger) - : base(state, memory) + public DeveloperLead([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger) + : base(state, memory, kernel) { - _kernel = kernel; _logger = logger; } @@ -64,8 +61,10 @@ public async Task CreatePlan(string ask) { // TODO: Ask the architect for the existing high level architecture // as well as the file structure - var context = new KernelArguments { ["input"] = AppendChatHistory(ask)}; - return await CallFunction(DevLeadSkills.Plan, context, _kernel); + var context = new KernelArguments { ["input"] = AppendChatHistory(ask) }; + var instruction = "Consider the following architectural guidelines:!waf!"; + var enhancedContext = await AddKnowledge(instruction, "waf", context); + return await CallFunction(DevLeadSkills.Plan, enhancedContext); } catch (Exception ex) { @@ -77,7 +76,7 @@ public async Task CreatePlan(string ask) public interface ILeadDevelopers { - public Task CreatePlan(string ask); + public Task CreatePlan(string ask); } [GenerateSerializer] diff --git a/src/sample-apps/gh-flow/Agents/ProductManager/ProductManager.cs b/src/sample-apps/gh-flow/Agents/ProductManager/ProductManager.cs index b577e00cfec..fa1accf4d56 100644 --- a/src/sample-apps/gh-flow/Agents/ProductManager/ProductManager.cs +++ b/src/sample-apps/gh-flow/Agents/ProductManager/ProductManager.cs @@ -1,24 +1,21 @@ using Microsoft.AI.Agents.Abstractions; using Microsoft.AI.DevTeam.Events; -using Microsoft.KernelMemory; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Memory; using Orleans.Runtime; using Orleans.Streams; namespace Microsoft.AI.DevTeam; [ImplicitStreamSubscription(Consts.MainNamespace)] -public class ProductManager : AzureAiAgent, IManageProducts +public class ProductManager : AiAgent, IManageProducts { protected override string Namespace => Consts.MainNamespace; - private readonly Kernel _kernel; private readonly ILogger _logger; - public ProductManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, IKernelMemory memory, ILogger logger) - : base(state, memory) + public ProductManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger) + : base(state, memory, kernel) { - _kernel = kernel; - //_memory = memory; _logger = logger; } @@ -63,7 +60,9 @@ public async Task CreateReadme(string ask) try { var context = new KernelArguments { ["input"] = AppendChatHistory(ask)}; - return await CallFunction(PMSkills.Readme, context, _kernel); + var instruction = "Consider the following architectural guidelines:!waf!"; + var enhancedContext = await AddKnowledge(instruction, "waf",context); + return await CallFunction(PMSkills.Readme, enhancedContext); } catch (Exception ex) { diff --git a/src/sample-apps/gh-flow/Program.cs b/src/sample-apps/gh-flow/Program.cs index 02b9c379aec..6c6841a94e1 100644 --- a/src/sample-apps/gh-flow/Program.cs +++ b/src/sample-apps/gh-flow/Program.cs @@ -9,12 +9,14 @@ using Azure.Identity; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Http.Resilience; -using Microsoft.KernelMemory; +using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Connectors.Qdrant; +using Microsoft.SemanticKernel.Connectors.OpenAI; var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton(); builder.Services.AddTransient(CreateKernel); -builder.Services.AddSingleton(CreateMemory); +builder.Services.AddTransient(CreateMemory); builder.Services.AddHttpClient(); builder.Services.AddSingleton(s => @@ -22,7 +24,7 @@ var ghOptions = s.GetService>(); var logger = s.GetService>(); var ghService = new GithubAuthService(ghOptions, logger); - var client = ghService.GetGitHubClient().Result; + var client = ghService.GetGitHubClient(); return client; }); @@ -96,29 +98,24 @@ app.Run(); -static IKernelMemory CreateMemory(IServiceProvider provider) +static ISemanticTextMemory CreateMemory(IServiceProvider provider) { - var qdrantConfig = provider.GetService>().Value; var openAiConfig = provider.GetService>().Value; - return new KernelMemoryBuilder() - .WithQdrantMemoryDb(qdrantConfig.Endpoint) - .WithAzureOpenAITextGeneration(new AzureOpenAIConfig - { - APIType = AzureOpenAIConfig.APITypes.ChatCompletion, - Endpoint = openAiConfig.Endpoint, - Deployment = openAiConfig.DeploymentOrModelId, - Auth = AzureOpenAIConfig.AuthTypes.APIKey, - APIKey = openAiConfig.ApiKey - }) - .WithAzureOpenAITextEmbeddingGeneration(new AzureOpenAIConfig - { - APIType = AzureOpenAIConfig.APITypes.EmbeddingGeneration, - Endpoint = openAiConfig.Endpoint, - Deployment =openAiConfig.EmbeddingDeploymentOrModelId, - Auth = AzureOpenAIConfig.AuthTypes.APIKey, - APIKey = openAiConfig.ApiKey - }) - .Build(); + var qdrantConfig = provider.GetService>().Value; + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .SetMinimumLevel(LogLevel.Debug) + .AddConsole() + .AddDebug(); + }); + + var memoryBuilder = new MemoryBuilder(); + return memoryBuilder.WithLoggerFactory(loggerFactory) + .WithQdrantMemoryStore(qdrantConfig.Endpoint, qdrantConfig.VectorSize) + .WithAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingDeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey) + .Build(); } static Kernel CreateKernel(IServiceProvider provider) diff --git a/src/sample-apps/gh-flow/Services/GithubAuthService.cs b/src/sample-apps/gh-flow/Services/GithubAuthService.cs index 974349fd11b..0c964d9dba2 100644 --- a/src/sample-apps/gh-flow/Services/GithubAuthService.cs +++ b/src/sample-apps/gh-flow/Services/GithubAuthService.cs @@ -44,7 +44,7 @@ public string GenerateJwtToken(string appId, string appKey, int minutes) return new JwtSecurityTokenHandler().WriteToken(token); } - public async Task GetGitHubClient() + public GitHubClient GetGitHubClient() { try { @@ -53,7 +53,7 @@ public async Task GetGitHubClient() { Credentials = new Credentials(jwtToken, AuthenticationType.Bearer) }; - var response = await appClient.GitHubApps.CreateInstallationToken(_githubSettings.InstallationId); + var response = appClient.GitHubApps.CreateInstallationToken(_githubSettings.InstallationId).Result; return new GitHubClient(new ProductHeaderValue($"SK-DEV-APP-Installation{_githubSettings.InstallationId}")) { Credentials = new Credentials(response.Token) diff --git a/src/sample-apps/gh-flow/gh-flow.csproj b/src/sample-apps/gh-flow/gh-flow.csproj index d79c690815a..85e09afff35 100644 --- a/src/sample-apps/gh-flow/gh-flow.csproj +++ b/src/sample-apps/gh-flow/gh-flow.csproj @@ -21,7 +21,6 @@ - @@ -45,7 +44,7 @@ - + diff --git a/src/sample-apps/seed-memory/Program.cs b/src/sample-apps/seed-memory/Program.cs index 7f1385ffbb5..af9fc428c32 100644 --- a/src/sample-apps/seed-memory/Program.cs +++ b/src/sample-apps/seed-memory/Program.cs @@ -1,12 +1,17 @@ -using Microsoft.Extensions.Logging; -using Microsoft.KernelMemory; +using System.Reflection; +using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Microsoft.SemanticKernel.Connectors.Qdrant; +using Microsoft.SemanticKernel.Memory; +using UglyToad.PdfPig; +using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor; class Program { static string WafFileName = "azure-well-architected.pdf"; static async Task Main(string[] args) { - var kernelSettings = KernelSettings.LoadSettings(); + var kernelSettings = KernelSettings.LoadSettings(); using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { @@ -15,34 +20,38 @@ static async Task Main(string[] args) .AddConsole() .AddDebug(); }); + + var memoryBuilder = new MemoryBuilder(); + var memory = memoryBuilder.WithLoggerFactory(loggerFactory) + .WithQdrantMemoryStore(kernelSettings.QdrantEndpoint, 1536) + .WithAzureOpenAITextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId,kernelSettings.Endpoint, kernelSettings.ApiKey) + .Build(); - var memory = new KernelMemoryBuilder() - .WithQdrantMemoryDb(kernelSettings.QdrantEndpoint) - .WithAzureOpenAITextGeneration(new AzureOpenAIConfig - { - APIType = AzureOpenAIConfig.APITypes.ChatCompletion, - Endpoint =kernelSettings.Endpoint, - Deployment = kernelSettings.DeploymentOrModelId, - Auth = AzureOpenAIConfig.AuthTypes.APIKey, - APIKey = kernelSettings.ApiKey - }) - .WithAzureOpenAITextEmbeddingGeneration(new AzureOpenAIConfig - { - APIType = AzureOpenAIConfig.APITypes.EmbeddingGeneration, - Endpoint = kernelSettings.Endpoint, - Deployment =kernelSettings.EmbeddingDeploymentOrModelId, - Auth = AzureOpenAIConfig.AuthTypes.APIKey, - APIKey = kernelSettings.ApiKey - }) - .Build(); await ImportDocumentAsync(memory, WafFileName); } - public static async Task ImportDocumentAsync(IKernelMemory memory, string filename) + public static async Task ImportDocumentAsync(ISemanticTextMemory memory, string filename) { - await memory.ImportDocumentAsync(new Document("wafdoc") - .AddFiles([ - filename - ]), index: "waf"); + var currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var filePath = Path.Combine(currentDirectory, filename); + using var pdfDocument = PdfDocument.Open(File.OpenRead(filePath)); + var pages = pdfDocument.GetPages(); + foreach (var page in pages) + { + try + { + var text = ContentOrderTextExtractor.GetText(page); + var descr = text.Take(100); + await memory.SaveInformationAsync( + collection: "waf", + text: text, + id: $"{Guid.NewGuid()}", + description: $"Document: {descr}"); + } + catch(Exception ex) + { + Console.WriteLine(ex.Message); + } + } } } \ No newline at end of file diff --git a/src/sample-apps/seed-memory/config/KernelSettings.cs b/src/sample-apps/seed-memory/config/KernelSettings.cs index f42f1bc918d..18f956f50ce 100644 --- a/src/sample-apps/seed-memory/config/KernelSettings.cs +++ b/src/sample-apps/seed-memory/config/KernelSettings.cs @@ -72,7 +72,7 @@ internal static KernelSettings FromFile(string configFile = DefaultConfigFile) } var configuration = new ConfigurationBuilder() - .SetBasePath(System.IO.Directory.GetCurrentDirectory()) + .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile(configFile, optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); diff --git a/src/sample-apps/seed-memory/seed-memory.csproj b/src/sample-apps/seed-memory/seed-memory.csproj index 47642e49d86..532fd2d799a 100644 --- a/src/sample-apps/seed-memory/seed-memory.csproj +++ b/src/sample-apps/seed-memory/seed-memory.csproj @@ -11,8 +11,14 @@ - - + + + + + + + +