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() { + + + + {selectedItem.name} + + + + + {sortData.map((item) => ( + + {({selected}) => ( + + {item.name} + + )} + + ))} + + + + + ); +} + +// Component: RiskTolerance +export function ReasoningEngine() { + + return ( + + + Reasoning Engine + + + + ); +} \ No newline at end of file diff --git a/ui/typescript/src/data/personalize/store.ts b/ui/typescript/src/data/personalize/store.ts index fa52b29b..3367ce25 100644 --- a/ui/typescript/src/data/personalize/store.ts +++ b/ui/typescript/src/data/personalize/store.ts @@ -1,6 +1,6 @@ import { atom } from 'jotai'; import {TopInvestmentsData} from "@/data/static/top-investments-data"; -import {AdvisorsList, RiskLevelsList, BooksList} from "@/data/static/personalize"; +import {AdvisorsList, RiskLevelsList, BooksList, ReasoningEngineListData} from "@/data/static/personalize"; import {UserInfo} from "@/data/static/user-info"; import {AssetsData} from "@/data/static/assetsData"; import {Chats} from "@/data/static/chats"; @@ -19,6 +19,8 @@ export const selectedBookAtom = atom(BooksList[0]); export const selectedRiskLevelAtom = atom(RiskLevelsList[getRandomIndex(RiskLevelsList.length)]); +export const selectedReasoningEngineAtom = atom(ReasoningEngineListData[0]); + export const chatsAtom = atom(Chats); export const chatSessionsAtom = atom([]); diff --git a/ui/typescript/src/data/static/personalize.tsx b/ui/typescript/src/data/static/personalize.tsx index 29845a68..dacf3bcd 100644 --- a/ui/typescript/src/data/static/personalize.tsx +++ b/ui/typescript/src/data/static/personalize.tsx @@ -17,4 +17,10 @@ export const AdvisorsList = [ {id: 6, name: 'Kevin Hart'}, {id: 7, name: 'Howard Marks'}, {id: 8, name: 'Warren Buffet'} +]; + +export const ReasoningEngineListData = [ + {id: 1, name: 'GPT-35-turbo'}, + {id: 2, name: 'Fine-tuned Llama-2-7B'}, + {id: 3, name: 'Llama-2-13B'}, ]; \ No newline at end of file