From 9f4ab501286902edf63bc3ae972a7369542c904e Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Mon, 6 Nov 2023 09:08:44 -0800 Subject: [PATCH 1/2] force kestrel to use http - fix for docker local runs --- .../dotnet/Controllers/AssetsController.cs | 4 ++-- .../dotnet/Controllers/InvestmentsController.cs | 4 ++-- services/recommendation-service/dotnet/Program.cs | 7 +++++++ services/recommendation-service/dotnet/Utils/RepoUtils.cs | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/services/recommendation-service/dotnet/Controllers/AssetsController.cs b/services/recommendation-service/dotnet/Controllers/AssetsController.cs index 3b655ace..8f26fa25 100644 --- a/services/recommendation-service/dotnet/Controllers/AssetsController.cs +++ b/services/recommendation-service/dotnet/Controllers/AssetsController.cs @@ -45,7 +45,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy sw.Start(); // ========= Import semantic functions as plugins ========= log.LogDebug("Path: {S}", Directory.GetCurrentDirectory()); - var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Plugins"); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); var advisorPlugin = _kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, "AdvisorPlugin"); // ========= Import native function ========= @@ -95,7 +95,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy public async Task GetRecommendationsRunAsync([FromBody] MiyagiContext miyagiContext) { // ========= Import semantic functions as plugins ========= - var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Plugins"); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); var advisorPlugin = _kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, "AdvisorPlugin"); // ========= Import native function ========= diff --git a/services/recommendation-service/dotnet/Controllers/InvestmentsController.cs b/services/recommendation-service/dotnet/Controllers/InvestmentsController.cs index 8d9695be..aaeb83b4 100644 --- a/services/recommendation-service/dotnet/Controllers/InvestmentsController.cs +++ b/services/recommendation-service/dotnet/Controllers/InvestmentsController.cs @@ -44,7 +44,7 @@ public async Task GetRecommendationsWithPlanner([FromBody] Miyagi log.BeginScope("InvestmentController.GetRecommendationsAsync"); // ========= Import Advisor skill from local filesystem ========= log.LogDebug("Path: {P}", Directory.GetCurrentDirectory()); - var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Plugins"); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); _kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, "AdvisorPlugin"); _kernel.ImportFunctions(new UserProfilePlugin(), "UserProfilePlugin"); var memoryCollection = _kernelSettings.CollectionName; @@ -105,7 +105,7 @@ public async Task GetRecommendations([FromBody] MiyagiContext miy log.BeginScope("InvestmentController.GetRecommendationsAsync"); // ========= Import Advisor skill from local filesystem ========= log.LogDebug("Path: {P}", Directory.GetCurrentDirectory()); - var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Plugins"); + var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); var advisorPlugin = _kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, "AdvisorPlugin"); var userProfilePlugin = _kernel.ImportFunctions(new UserProfilePlugin(), "UserProfilePlugin"); var memoryCollection = _kernelSettings.CollectionName; diff --git a/services/recommendation-service/dotnet/Program.cs b/services/recommendation-service/dotnet/Program.cs index 39874d5b..e2d0b891 100644 --- a/services/recommendation-service/dotnet/Program.cs +++ b/services/recommendation-service/dotnet/Program.cs @@ -21,6 +21,13 @@ // Add Semantic Kernel services builder.Services.AddSkServices(); +// Explicitly configure Kestrel to only use HTTP +builder.WebHost.ConfigureKestrel(serverOptions => +{ + // Remove the HTTPS endpoint if it's there + serverOptions.ListenAnyIP(80); // Listen on port 80 for HTTP +}); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/services/recommendation-service/dotnet/Utils/RepoUtils.cs b/services/recommendation-service/dotnet/Utils/RepoUtils.cs index b1b1cadc..4615aff4 100644 --- a/services/recommendation-service/dotnet/Utils/RepoUtils.cs +++ b/services/recommendation-service/dotnet/Utils/RepoUtils.cs @@ -9,13 +9,13 @@ namespace GBB.Miyagi.RecommendationService.Utils; public static class RepoUtils { /// - /// Scan the local folders from the repo, looking for "Plugins" folder. + /// Scan the local folders from the repo, looking for "plugins" folder. /// /// The full path to samples/skills public static string GetSamplePluginsPath() { const string Parent = ""; - const string Folder = "Plugins"; + const string Folder = "plugins"; bool SearchPath(string pathToFind, out string result, int maxAttempts = 10) { From b8c4ae4bcee9136b63712b5fc26de9b16ee214bf Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Mon, 6 Nov 2023 16:00:01 -0800 Subject: [PATCH 2/2] remove frontend proxy and use cors allowed list in backend --- .../recommendation-service/dotnet/Program.cs | 17 +++++++++---- .../dotnet/config/KernelSettings.cs | 3 +++ ui/typescript/.env | 2 +- ui/typescript/.env.local.example | 2 +- ui/typescript/app/chat-session/route.ts | 4 ++-- .../app/chatSession/getAllChats/route.ts | 4 ++-- ui/typescript/app/personalize/route.ts | 4 ++-- .../personalize/personalize-drawer.tsx | 24 +++++++++++++++---- 8 files changed, 43 insertions(+), 17 deletions(-) diff --git a/services/recommendation-service/dotnet/Program.cs b/services/recommendation-service/dotnet/Program.cs index e2d0b891..c744342e 100644 --- a/services/recommendation-service/dotnet/Program.cs +++ b/services/recommendation-service/dotnet/Program.cs @@ -21,11 +21,16 @@ // Add Semantic Kernel services builder.Services.AddSkServices(); -// Explicitly configure Kestrel to only use HTTP -builder.WebHost.ConfigureKestrel(serverOptions => +// Configure CORS to allow specific origins from KernelSettings +builder.Services.AddCors(options => { - // Remove the HTTPS endpoint if it's there - serverOptions.ListenAnyIP(80); // Listen on port 80 for HTTP + options.AddPolicy(name: "MiyagiAllowSpecificOrigins", + policy => + { + policy.WithOrigins(kernelSettings.CorsAllowedOrigins) + .AllowAnyHeader() + .AllowAnyMethod(); + }); }); var app = builder.Build(); @@ -34,9 +39,11 @@ app.UseSwagger(); app.UseSwaggerUI(); -app.UseCors(); +app.UseCors("MiyagiAllowSpecificOrigins"); // app.UseHttpsRedirection(); // Issue with Next.js to use https redirection +app.UseRouting(); + app.Map("/", () => Results.Redirect("/swagger")); // app.UseAuthorization(); diff --git a/services/recommendation-service/dotnet/config/KernelSettings.cs b/services/recommendation-service/dotnet/config/KernelSettings.cs index 83fefec0..fef92c28 100644 --- a/services/recommendation-service/dotnet/config/KernelSettings.cs +++ b/services/recommendation-service/dotnet/config/KernelSettings.cs @@ -50,6 +50,9 @@ internal class KernelSettings [JsonPropertyName("cosmosDbConnectionString")] public string CosmosDbConnectionString { get; set; } = string.Empty; + [JsonPropertyName("corsAllowedOrigins")] + public string[] CorsAllowedOrigins { get; set; } = Array.Empty(); + /// /// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets. diff --git a/ui/typescript/.env b/ui/typescript/.env index 8782e0e3..48c97dba 100644 --- a/ui/typescript/.env +++ b/ui/typescript/.env @@ -1,3 +1,3 @@ NEXT_PUBLIC_GA_MEASUREMENT_ID=G-DC9QMEZBZL -RECCOMMENDATION_SERVICE_URL=http://localhost:5224 +NEXT_PUBLIC_RECCOMMENDATION_SERVICE_URL=http://localhost:5224 NEXT_PUBLIC_COPILOT_CHAT_BASE_URL=https://copilotdemo.app \ No newline at end of file diff --git a/ui/typescript/.env.local.example b/ui/typescript/.env.local.example index 5bdae45c..9da38b3d 100644 --- a/ui/typescript/.env.local.example +++ b/ui/typescript/.env.local.example @@ -1,5 +1,5 @@ NEXT_PUBLIC_GA_MEASUREMENT_ID= -RECCOMMENDATION_SERVICE_URL= +NEXT_PUBLIC_RECCOMMENDATION_SERVICE_URL= NEXT_PUBLIC_CHAT_ID= COPILOT_CHAT_BASE_URL= NEXT_PUBLIC_COPILOT_CHAT_BASE_URL= diff --git a/ui/typescript/app/chat-session/route.ts b/ui/typescript/app/chat-session/route.ts index 9dd9a03f..65100be9 100644 --- a/ui/typescript/app/chat-session/route.ts +++ b/ui/typescript/app/chat-session/route.ts @@ -3,10 +3,10 @@ import { NextResponse } from 'next/server'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const userId = searchParams.get('userId'); - const RECCOMMENDATION_SERVICE_URL = `${process.env.NEXT_PUBLIC_COPILOT_CHAT_BASE_URL}/chats` + const chatsUrl = `${process.env.NEXT_PUBLIC_COPILOT_CHAT_BASE_URL}/chats` console.log("Request body: "); console.dir(userId) - const res = await fetch(RECCOMMENDATION_SERVICE_URL, { + const res = await fetch(chatsUrl, { method: 'POST', headers: { 'Content-type': `application/json` diff --git a/ui/typescript/app/chatSession/getAllChats/route.ts b/ui/typescript/app/chatSession/getAllChats/route.ts index 9dd9a03f..65100be9 100644 --- a/ui/typescript/app/chatSession/getAllChats/route.ts +++ b/ui/typescript/app/chatSession/getAllChats/route.ts @@ -3,10 +3,10 @@ import { NextResponse } from 'next/server'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const userId = searchParams.get('userId'); - const RECCOMMENDATION_SERVICE_URL = `${process.env.NEXT_PUBLIC_COPILOT_CHAT_BASE_URL}/chats` + const chatsUrl = `${process.env.NEXT_PUBLIC_COPILOT_CHAT_BASE_URL}/chats` console.log("Request body: "); console.dir(userId) - const res = await fetch(RECCOMMENDATION_SERVICE_URL, { + const res = await fetch(chatsUrl, { method: 'POST', headers: { 'Content-type': `application/json` diff --git a/ui/typescript/app/personalize/route.ts b/ui/typescript/app/personalize/route.ts index c6627c74..05652fcf 100644 --- a/ui/typescript/app/personalize/route.ts +++ b/ui/typescript/app/personalize/route.ts @@ -2,11 +2,11 @@ import { NextResponse } from 'next/server'; export async function POST(request: Request) { console.dir(request); - const RECCOMMENDATION_SERVICE_URL = `${process.env.RECCOMMENDATION_SERVICE_URL}/personalize` + const personalizeUrl = `${process.env.NEXT_PUBLIC_RECCOMMENDATION_SERVICE_URL}/personalize` const { body } = request console.log("Request body: "); console.dir(body) - const res = await fetch(RECCOMMENDATION_SERVICE_URL, { + const res = await fetch(personalizeUrl, { method: 'POST', headers: { 'Content-type': `application/json` }, duplex: 'half', diff --git a/ui/typescript/src/components/personalize/personalize-drawer.tsx b/ui/typescript/src/components/personalize/personalize-drawer.tsx index 341b0edb..76be032c 100644 --- a/ui/typescript/src/components/personalize/personalize-drawer.tsx +++ b/ui/typescript/src/components/personalize/personalize-drawer.tsx @@ -76,18 +76,33 @@ export default function PersonalizeDrawer() { stocks, }; - const response = await personalizeMutation.mutateAsync(requestData); - console.log('Successfully got personalization'); + const response = await fetch(`${process.env.NEXT_PUBLIC_RECCOMMENDATION_SERVICE_URL}/personalize`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData), + }); + console.dir(response); + + if (!response.ok) { + console.error('HTTP error: ', response); + toast.error('Failed to fetch personalization. Try again later.'); + } + + const responseData = await response.json(); + console.log('Successfully got personalization'); + console.dir(responseData); toast.success('Personalization successful'); // Extract the relevant data from the response - const updatedAssetData = response.assets.portfolio.map((item, index) => ({ + const updatedAssetData = responseData.assets.portfolio.map((item, index) => ({ ...assetsInfo[index], gptRecommendation: item.gptRecommendation, })); - const updatedInvestmentData = response.investments.portfolio.map((item, index) => ({ + const updatedInvestmentData = responseData.investments.portfolio.map((item, index) => ({ ...investmentsInfo[index], gptRecommendation: item.gptRecommendation, })); @@ -107,6 +122,7 @@ export default function PersonalizeDrawer() { } }; + return (