diff --git a/services/recommendation-service/dotnet/controllers/InvestmentsController.cs b/services/recommendation-service/dotnet/controllers/InvestmentsController.cs index f2ec05b6..bfcd1fe0 100644 --- a/services/recommendation-service/dotnet/controllers/InvestmentsController.cs +++ b/services/recommendation-service/dotnet/controllers/InvestmentsController.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Text.Json; using GBB.Miyagi.RecommendationService.models; using GBB.Miyagi.RecommendationService.plugins; @@ -6,8 +7,10 @@ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.Tokenizers; using Microsoft.SemanticKernel.Orchestration; +using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.Skills.Core; using Microsoft.SemanticKernel.Skills.Web; +using Microsoft.SemanticKernel.Skills.Web.Bing; namespace GBB.Miyagi.RecommendationService.Controllers; @@ -30,11 +33,87 @@ public InvestmentsController(IKernel kernel, WebSearchEngineSkill webSearchEngin _webSearchEngineSkill = webSearchEngineSkill; } + + [HttpPost("/v2/investments")] + public async Task GetRecommendationsWithPlanner([FromBody] MiyagiContext miyagiContext) + { + var log = ConsoleLogger.Log; + log?.BeginScope("InvestmentController.GetRecommendationsAsync"); + // ========= Import Advisor skill from local filesystem ========= + log?.LogDebug("Path: {P}", Directory.GetCurrentDirectory()); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); + _kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "AdvisorPlugin"); + _kernel.ImportSkill(new UserProfilePlugin(), "UserProfilePlugin"); + + // ========= Fetch memory from vector store using recall ========= + _kernel.ImportSkill(new TextMemorySkill()); + + // ========= Set context variables for Advisor skill ========= + var context = new ContextVariables(); + context.Set("userId", miyagiContext.UserInfo.UserId); + context.Set("stocks", JsonSerializer.Serialize(miyagiContext.Stocks)); + context.Set("voice", miyagiContext.UserInfo.FavoriteAdvisor); + context.Set("risk", miyagiContext.UserInfo.RiskLevel); + context.Set("semanticQuery", $"Investment advise for {miyagiContext.UserInfo.RiskLevel} risk level"); + + context[TextMemorySkill.CollectionParam] = _memoryCollection; + //context[TextMemorySkill.KeyParam] = miyagiContext.UserInfo.FavoriteBook; + context[TextMemorySkill.RelevanceParam] = "0.8"; + context[TextMemorySkill.LimitParam] = "3"; + context.Set("tickers", miyagiContext.Stocks?.Select(s => s.Symbol).ToList().ToString()); + + // ========= Log token count, which determines cost ========= + var numTokens = GPT3Tokenizer.Encode(context.ToString()).Count; + log?.LogDebug("Number of Tokens: {N}", numTokens); + log?.LogDebug("Context: {S}", context.ToString()); + + // ========= Import Bing web search skill to augment current info ========= + _kernel.ImportSkill(_webSearchEngineSkill, "WebSearch"); + _kernel.ImportSkill(new TimeSkill(), "time"); + + var ask = "Given stocks, return investment advise, factoring current inflation from search?"; + + Console.WriteLine("*****************************************************"); + Stopwatch sw = new(); + Console.WriteLine("Question: " + ask); + + var plannerConfig = new Microsoft.SemanticKernel.Planning.Stepwise.StepwisePlannerConfig(); + plannerConfig.ExcludedFunctions.Add("PortfolioAllocation"); + plannerConfig.MinIterationTimeMs = 1500; + plannerConfig.MaxTokens = 3000; + + StepwisePlanner planner = new(_kernel, plannerConfig); + sw.Start(); + var plan = planner.CreatePlan(ask); + + var result = await plan.InvokeAsync(_kernel.CreateNewContext()); + Console.WriteLine("Result: " + result); + if (result.Variables.TryGetValue("stepCount", out string? stepCount)) + { + Console.WriteLine("Steps Taken: " + stepCount); + } + + if (result.Variables.TryGetValue("skillCount", out string? skillCount)) + { + Console.WriteLine("Skills Used: " + skillCount); + } + + Console.WriteLine("Time Taken: " + sw.Elapsed); + Console.WriteLine("*****************************************************"); + + var output = result.Result.Replace("\n", ""); + + var jsonDocument = JsonDocument.Parse(output); + + return new JsonResult(jsonDocument); + + } + [HttpPost("/investments")] public async Task GetRecommendations([FromBody] MiyagiContext miyagiContext) { var log = ConsoleLogger.Log; - log?.BeginScope("MemoryController.SaveDatasetAsync"); + log?.BeginScope("InvestmentController.GetRecommendationsAsync"); // ========= Import Advisor skill from local filesystem ========= log?.LogDebug("Path: {P}", Directory.GetCurrentDirectory()); var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); diff --git a/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/config.json b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/config.json index 521ff108..2a983cef 100644 --- a/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/config.json +++ b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/config.json @@ -1,7 +1,7 @@ { "schema": 1, "type": "completion", - "description": "Gives financial advise on how to allocate portfolio, given a risk tolerance and a set of assets", + "description": "Gives financial advise on how to allocate portfolio, given a risk tolerance and a set of stocks", "completion": { "max_tokens": 512, "temperature": 0.9, diff --git a/services/recommendation-service/dotnet/plugins/AdvisorPlugin/PortfolioAllocation/config.json b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/PortfolioAllocation/config.json index 8e8be1bb..c5537e02 100644 --- a/services/recommendation-service/dotnet/plugins/AdvisorPlugin/PortfolioAllocation/config.json +++ b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/PortfolioAllocation/config.json @@ -1,7 +1,7 @@ { "schema": 1, "type": "completion", - "description": "Gives financial advise on how to allocate portfolio, given a risk tolerance and a set of assets", + "description": "Gives financial advise on how to allocate assets, given a risk tolerance", "completion": { "max_tokens": 512, "temperature": 0.8,