diff --git a/services/recommendation-service/dotnet/GBB.Miyagi.RecommendationService.csproj b/services/recommendation-service/dotnet/GBB.Miyagi.RecommendationService.csproj index dbb5d924..0a9f21e5 100644 --- a/services/recommendation-service/dotnet/GBB.Miyagi.RecommendationService.csproj +++ b/services/recommendation-service/dotnet/GBB.Miyagi.RecommendationService.csproj @@ -10,15 +10,16 @@ - - - - - - - - - + + + + + + + + + + diff --git a/services/recommendation-service/dotnet/controllers/AssetsController.cs b/services/recommendation-service/dotnet/controllers/AssetsController.cs index 73f116d1..456d6db6 100644 --- a/services/recommendation-service/dotnet/controllers/AssetsController.cs +++ b/services/recommendation-service/dotnet/controllers/AssetsController.cs @@ -45,6 +45,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy Stopwatch sw = new(); sw.Start(); // ========= Import semantic functions as plugins ========= + log?.LogDebug("Path: {S}", Directory.GetCurrentDirectory()); var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); var advisorPlugin = _kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "AdvisorPlugin"); diff --git a/services/recommendation-service/dotnet/controllers/InvestmentsController.cs b/services/recommendation-service/dotnet/controllers/InvestmentsController.cs index 130a97ee..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,13 +33,90 @@ 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 ========= - var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Plugins"); + log?.LogDebug("Path: {P}", Directory.GetCurrentDirectory()); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); var advisorPlugin = _kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "AdvisorPlugin"); var userProfilePlugin = _kernel.ImportSkill(new UserProfilePlugin(), "UserProfilePlugin"); @@ -49,6 +129,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy 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; diff --git a/services/recommendation-service/dotnet/controllers/RecommendationsController.cs b/services/recommendation-service/dotnet/controllers/RecommendationsController.cs index 7d5b87e8..47bbc4c8 100644 --- a/services/recommendation-service/dotnet/controllers/RecommendationsController.cs +++ b/services/recommendation-service/dotnet/controllers/RecommendationsController.cs @@ -35,6 +35,7 @@ public class RecommendationsController : ControllerBase [HttpPost("/personalize")] public async Task GetRecommendations([FromBody] MiyagiContext miyagiContext) { + var log = ConsoleLogger.Log; const int maxRetries = 2; var currentRetry = 0; @@ -68,7 +69,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy // Handle error gracefully, e.g. return an error response return BadRequest(new { error = "Failed to parse JSON data after retries" }); } - + log?.LogError("Failed to parse JSON data: {S}", ex.Message); currentRetry++; } } @@ -95,9 +96,9 @@ public async Task GetRecommendationsSample([FromBody] MiyagiConte var web = _kernel.ImportSkill(_webSearchEngineSkill); - var skillsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Skills"); + var skillsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); - var advisorSkill = _kernel.ImportSemanticSkillFromDirectory(skillsDirectory, "AdvisorSkill"); + var advisorSkill = _kernel.ImportSemanticSkillFromDirectory(skillsDirectory, "AdvisorPlugin"); var context = new ContextVariables(); 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/InvestmentAdvise/skprompt.txt b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/skprompt.txt index 7173726c..74ad1520 100644 --- a/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/skprompt.txt +++ b/services/recommendation-service/dotnet/plugins/AdvisorPlugin/InvestmentAdvise/skprompt.txt @@ -15,8 +15,7 @@ Example: {"portfolio":[{"symbol":"MSFT","gptRecommendation":"Booyah! Hold on, st current inflation and mortgage rates: {{$bingResults}} -wisdom of crowds opinions: {{recall "investment advise"} - +Favorite book's advise: {{recall $semanticQuery}} System: { 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, diff --git a/ui/typescript/src/components/personalize/personalize-drawer.tsx b/ui/typescript/src/components/personalize/personalize-drawer.tsx index 9e6ec469..969f7447 100644 --- a/ui/typescript/src/components/personalize/personalize-drawer.tsx +++ b/ui/typescript/src/components/personalize/personalize-drawer.tsx @@ -23,6 +23,7 @@ import { userInfoAtom } from "@/data/personalize/store"; import {formatRequestData} from "@/data/utils/format-request-data"; +import {ReasoningEngine} from "@/components/personalize/selectors/reasoning-engine-list"; // Component: B2CLogin @@ -153,6 +154,7 @@ export default function PersonalizeDrawer() { +