Skip to content

Commit

Permalink
Merge pull request #126 from Azure-Samples/gk/64-auto-invest-using-pl…
Browse files Browse the repository at this point in the history
…anner
  • Loading branch information
thegovind committed Jul 24, 2023
2 parents ef167c8 + fc10881 commit dc2765d
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.17.0-beta.1"/>
<PackageReference Include="Confluent.Kafka" Version="2.1.0"/>
<PackageReference Include="Microsoft.SemanticKernel" Version="0.17.230711.7-preview"/>
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Memory.Qdrant" Version="0.17.230711.7-preview"/>
<PackageReference Include="Microsoft.SemanticKernel.Planning.StepwisePlanner" Version="0.17.230711.7-preview"/>
<PackageReference Include="Microsoft.SemanticKernel.Skills.Web" Version="0.17.230711.7-preview"/>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
<PackageReference Include="Azure.Storage.Blobs" Version="12.17.0-beta.1" />
<PackageReference Include="Confluent.Kafka" Version="2.1.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="0.17.230718.1-preview" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Memory.AzureSearch" Version="0.17.230718.1-preview" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Memory.Qdrant" Version="0.17.230718.1-preview" />
<PackageReference Include="Microsoft.SemanticKernel.Planning.StepwisePlanner" Version="0.17.230718.1-preview" />
<PackageReference Include="Microsoft.SemanticKernel.Skills.Web" Version="0.17.230718.1-preview" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public async Task<IActionResult> 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");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Text.Json;
using GBB.Miyagi.RecommendationService.models;
using GBB.Miyagi.RecommendationService.plugins;
Expand All @@ -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;

Expand All @@ -30,13 +33,90 @@ public InvestmentsController(IKernel kernel, WebSearchEngineSkill webSearchEngin
_webSearchEngineSkill = webSearchEngineSkill;
}


[HttpPost("/v2/investments")]
public async Task<IActionResult> 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<IActionResult> 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");

Expand All @@ -49,6 +129,7 @@ public async Task<IActionResult> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class RecommendationsController : ControllerBase
[HttpPost("/personalize")]
public async Task<IActionResult> GetRecommendations([FromBody] MiyagiContext miyagiContext)
{
var log = ConsoleLogger.Log;
const int maxRetries = 2;
var currentRetry = 0;

Expand Down Expand Up @@ -68,7 +69,7 @@ public async Task<IActionResult> 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++;
}
}
Expand All @@ -95,9 +96,9 @@ public async Task<IActionResult> 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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -153,6 +154,7 @@ export default function PersonalizeDrawer() {
<FavoriteBookSelector/>
<FavoriteAdvisorSelector/>
<RiskTolerance/>
<ReasoningEngine/>

<Button
size="large"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {ReasoningEngineListData} from "@/data/static/personalize";
import {KeyValueListProp} from "@/types";
import {Fragment} from "react";
import {Listbox} from "@/components/ui/listbox";
import cn from "classnames";
import {ChevronDownIcon} from "@heroicons/react/24/outline";
import {Transition} from "@/components/ui/transition";
import {useAtom} from "jotai";
import {selectedReasoningEngineAtom, selectedRiskLevelAtom} from "@/data/personalize/store";

export function ReasoningEngineList({
sortData,
className,
}: {
sortData: KeyValueListProp[];
className?: string;
}) {
const [selectedItem, setSelectedReasoningEngine] = useAtom(selectedReasoningEngineAtom);
return (
<div className="relative w-full lg:w-auto">
<Listbox value={selectedItem} onChange={setSelectedReasoningEngine}>
<Listbox.Button
className={cn(
'flex h-11 w-full items-center justify-between gap-1 rounded-lg bg-slate-600/80 px-3 text-sm text-white',
className
)}
>
{selectedItem.name}
<ChevronDownIcon className="h-auto w-6"/>
</Listbox.Button>
<Transition
as={Fragment}
enter="ease-out duration-200"
enterFrom="opacity-0 translate-y-2"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 -translate-y-0"
leaveTo="opacity-0 translate-y-2"
>
<Listbox.Options
className="absolute z-20 mt-2 w-full min-w-[150px] origin-top-right rounded-lg bg-white p-3 px-1.5 shadow-large shadow-gray-400/10 ltr:right-0 rtl:left-0 dark:bg-[rgba(0,0,0,0.5)] dark:shadow-gray-900 dark:backdrop-blur">
{sortData.map((item) => (
<Listbox.Option key={item.id} value={item}>
{({selected}) => (
<div
className={`block cursor-pointer rounded-lg px-3 py-2 text-sm font-medium text-gray-900 transition dark:text-white ${
selected
? 'my-1 bg-gray-100 dark:bg-gray-700'
: 'hover:bg-gray-50 dark:hover:bg-gray-700'
}`}
>
{item.name}
</div>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</Listbox>
</div>
);
}

// Component: RiskTolerance
export function ReasoningEngine() {

return (
<div className="px-6 pt-8">
<h4 className="mb-4 text-sm font-medium text-gray-900 dark:text-white">
Reasoning Engine
</h4>
<ReasoningEngineList sortData={ReasoningEngineListData}/>
</div>
);
}
4 changes: 3 additions & 1 deletion ui/typescript/src/data/personalize/store.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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<any[]>([]);
Expand Down
6 changes: 6 additions & 0 deletions ui/typescript/src/data/static/personalize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'},
];

0 comments on commit dc2765d

Please sign in to comment.