From ff6059dc7eb1c22f826a76c3d0099404e716373d Mon Sep 17 00:00:00 2001 From: amitjoshi438 <54068463+amitjoshi438@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:17:45 +0530 Subject: [PATCH] [PowerPage][Copilot] Nl2Site and Nl2Page Service Integration for Site Create (#1053) * Refactor PowerPagesChatParticipantConstants and add NL2PAGE and NL2SITE constants * Refactor PowerPagesChatParticipantConstants and add new page types * Update constants and move telemetry to different file * Refactor and add getCommonHeaders function --------- Co-authored-by: amitjoshi --- .../powerpages/PowerPagesChatParticipant.ts | 5 +- .../PowerPagesChatParticipantConstants.ts | 28 +++--- ...rPagesChatParticipantTelemetryConstants.ts | 22 +++++ .../commands/create-site/Nl2PageService.ts | 91 +++++++++++++++++++ .../commands/create-site/Nl2SiteService.ts | 54 +++++++++++ 5 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts create mode 100644 src/common/chat-participants/powerpages/commands/create-site/Nl2PageService.ts create mode 100644 src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index 163fc193..3a6d9a08 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -12,7 +12,7 @@ import { sendApiRequest } from '../../copilot/IntelligenceApiService'; import { PacWrapper } from '../../../client/pac/PacWrapper'; import { intelligenceAPIAuthentication } from '../../services/AuthenticationProvider'; import { ActiveOrgOutput } from '../../../client/pac/PacTypes'; -import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; +import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils'; import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables } from './PowerPagesChatParticipantUtils'; import { checkCopilotAvailability, fetchRelatedFiles, getActiveEditorContent } from '../../utilities/Utils'; @@ -23,6 +23,7 @@ import { isPowerPagesGitHubCopilotEnabled } from '../../copilot/utils/copilotUti import { ADX_WEBPAGE, IApiRequestParams, IRelatedFiles } from '../../constants'; import { oneDSLoggerWrapper } from '../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; import { CommandRegistry } from '../CommandRegistry'; +import { VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR } from './PowerPagesChatParticipantTelemetryConstants'; // Initialize Command Registry and Register Commands const commandRegistry = new CommandRegistry(); @@ -169,7 +170,7 @@ export class PowerPagesChatParticipant { powerPagesAgentSessionId: this.powerPagesAgentSessionId, telemetry: this.telemetry }; - + return await command.execute(commandRequest, stream); } else { diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index 969f5bc5..2145a6e3 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts @@ -42,20 +42,14 @@ export const DISCLAIMER_MESSAGE = vscode.l10n.t('Make sure AI-generated content export const NO_PROMPT_MESSAGE = vscode.l10n.t('Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.'); export const PAC_AUTH_INPUT = vscode.l10n.t("Checking for active auth profile..."); export const COPILOT_NOT_RELEASED_MSG = vscode.l10n.t("@PowerPages is not yet available in your region.") - -// Telemetry Event Names -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED = 'VSCodeExtensionGitHubPowerPagesAgentInvoked'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS = 'VSCodeExtensionGitHubPowerPagesAgentOrgDetails'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND = 'VSCodeExtensionGitHubPowerPagesAgentOrgDetailsNotFound'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO = 'VSCodeExtensionGitHubPowerPagesAgentScenario'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSUP = 'VSCodeExtensionGitHubPowerPagesAgentScenarioFeedbackThumbsUp'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSDOWN = 'VSCodeExtensionGitHubPowerPagesAgentScenarioFeedbackThumbsDown'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR = 'VSCodeExtensionGitHubPowerPagesAgentError'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES = 'VSCodeExtensionGitHubPowerPagesAgentWebpageRelatedFiles'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED = 'VSCodeExtensionGitHubPowerPagesAgentLocationReferenced'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentNoPrompt'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentWelcomePrompt'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentSuccessfulPrompt'; -export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS = 'VSCodeExtensionGitHubPowerPagesAgentNotAvailableECS'; - - +export const NL2PAGE_REQUEST_FAILED = 'Request failed for page type:'; +export const NL2SITE_INVALID_RESPONSE = 'Invalid response structure'; +export const HOME_PAGE_TYPE = 'Home'; +export const ABOUT_PAGE_TYPE = 'AboutUs'; +export const FAQ_PAGE_TYPE = 'FAQ'; +export const INFO_PAGE_TYPE = 'Informational'; +export const NL2PAGE_SCENARIO = 'NL2Page'; +export const NL2SITE_SCENARIO = 'NL2Site'; +export const NL2PAGE_GENERATE_NEW_PAGE = 'GenerateNewPage'; +export const NL2SITE_GENERATE_NEW_SITE = 'GenerateNewSite'; +export const NL2PAGE_SCOPE = 'Page'; diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts new file mode 100644 index 00000000..368a2284 --- /dev/null +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED = 'VSCodeExtensionGitHubPowerPagesAgentInvoked'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS = 'VSCodeExtensionGitHubPowerPagesAgentOrgDetails'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND = 'VSCodeExtensionGitHubPowerPagesAgentOrgDetailsNotFound'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO = 'VSCodeExtensionGitHubPowerPagesAgentScenario'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSUP = 'VSCodeExtensionGitHubPowerPagesAgentScenarioFeedbackThumbsUp'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSDOWN = 'VSCodeExtensionGitHubPowerPagesAgentScenarioFeedbackThumbsDown'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR = 'VSCodeExtensionGitHubPowerPagesAgentError'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES = 'VSCodeExtensionGitHubPowerPagesAgentWebpageRelatedFiles'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED = 'VSCodeExtensionGitHubPowerPagesAgentLocationReferenced'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentNoPrompt'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentWelcomePrompt'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentSuccessfulPrompt'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS = 'VSCodeExtensionGitHubPowerPagesAgentNotAvailableECS'; +export const VSCODE_EXTENSION_NL2PAGE_REQUEST_FAILED = 'VSCodeExtensionNL2PageRequestFailed'; +export const VSCODE_EXTENSION_NL2PAGE_REQUEST_SUCCESS = 'VSCodeExtensionNL2PageRequestSuccess'; +export const VSCODE_EXTENSION_NL2SITE_REQUEST_FAILED = 'VSCodeExtensionNL2SiteRequestFailed'; +export const VSCODE_EXTENSION_NL2SITE_REQUEST_SUCCESS = 'VSCodeExtensionNL2SiteRequestSuccess'; diff --git a/src/common/chat-participants/powerpages/commands/create-site/Nl2PageService.ts b/src/common/chat-participants/powerpages/commands/create-site/Nl2PageService.ts new file mode 100644 index 00000000..f410f0fd --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/Nl2PageService.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ITelemetry } from "../../../../OneDSLoggerTelemetry/telemetry/ITelemetry"; +import { getCommonHeaders } from "../../../../services/AuthenticationProvider"; +import { ABOUT_PAGE_TYPE, FAQ_PAGE_TYPE, HOME_PAGE_TYPE, INFO_PAGE_TYPE, NL2PAGE_GENERATE_NEW_PAGE, NL2PAGE_REQUEST_FAILED, NL2PAGE_SCENARIO, NL2PAGE_SCOPE} from "../../PowerPagesChatParticipantConstants"; +import { VSCODE_EXTENSION_NL2PAGE_REQUEST_FAILED, VSCODE_EXTENSION_NL2PAGE_REQUEST_SUCCESS } from "../../PowerPagesChatParticipantTelemetryConstants"; + +export async function getNL2PageData(aibEndpoint: string, aibToken: string, userPrompt: string, siteName: string, sitePagesList: string[], sessionId: string, telemetry: ITelemetry) { + + const constructRequestBody = (pageType: string, colorNumber:number, exampleNumber: number) => ({ + "crossGeoOptions": { + "enableCrossGeoCall": true + }, + "question": `${userPrompt} - ${pageType} page`, + "context": { + "shouldCheckBlockList": false, + "sessionId": sessionId, + "scenario": NL2PAGE_SCENARIO, + "subScenario": NL2PAGE_GENERATE_NEW_PAGE, + "version": "V1", + "information": { + "scope": NL2PAGE_SCOPE, + "includeImages": true, + "pageType": pageType === 'FAQ' ? 'FAQ' : 'Home', //Verify if this is correct + "title": siteName, + "pageName": pageType, + "colorNumber": colorNumber, + "shuffleImages": false, + "exampleNumber": exampleNumber + } + } + }); + + const requests = sitePagesList.map(async pageType => { + const colorNumber = generateRandomColorNumber(); + const exampleNumber = generateRandomExampleNumber(pageType); + const requestBody = constructRequestBody(pageType, colorNumber, exampleNumber); + + const requestInit: RequestInit = { + method: "POST", + headers: getCommonHeaders(aibToken), + body: JSON.stringify(requestBody) + }; + + try { + const response = await fetch(aibEndpoint, requestInit); + if (!response.ok) { + throw new Error(`${NL2PAGE_REQUEST_FAILED} ${pageType}`); + } + + const responseData = await response.json(); + + if (responseData && responseData.additionalData[0]) { + return responseData.additionalData[0].snippets[0]; + } + return null; + } catch (error) { + telemetry.sendTelemetryErrorEvent(VSCODE_EXTENSION_NL2PAGE_REQUEST_FAILED, { error: (error as Error)?.message, pageType }); + return null; + } + }); + + const responses = await Promise.all(requests); + + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_NL2PAGE_REQUEST_SUCCESS, { sessionId }); + + // TODO: Home page is mandatory, so if it is not generated, return null + return responses.filter(response => response !== null); +} + +export const generateRandomColorNumber = () => { + const colorNumbers = [1, 2, 3, 5, 6, 7, 8]; + return colorNumbers[Math.floor(Math.random() * colorNumbers.length)]; + }; + + export const generateRandomExampleNumber = (pageType: string) => { + const isFaqOrAboutPage = pageType === FAQ_PAGE_TYPE || pageType === ABOUT_PAGE_TYPE; + if (isFaqOrAboutPage) { + return 0; + } else if (pageType === HOME_PAGE_TYPE) { + const homeExampleNumbers = [1, 2, 3, 4]; + return homeExampleNumbers[Math.floor(Math.random() * homeExampleNumbers.length)]; + } else if (pageType === INFO_PAGE_TYPE) { + const infoExampleNumbers = [1, 2, 3]; + return infoExampleNumbers[Math.floor(Math.random() * infoExampleNumbers.length)]; + } + return 0; + }; diff --git a/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts new file mode 100644 index 00000000..20a1a8e8 --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts @@ -0,0 +1,54 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ITelemetry } from "../../../../OneDSLoggerTelemetry/telemetry/ITelemetry"; +import { NL2SITE_GENERATE_NEW_SITE, NL2SITE_INVALID_RESPONSE, NL2SITE_SCENARIO} from "../../PowerPagesChatParticipantConstants"; +import { VSCODE_EXTENSION_NL2SITE_REQUEST_SUCCESS, VSCODE_EXTENSION_NL2SITE_REQUEST_FAILED } from "../../PowerPagesChatParticipantTelemetryConstants"; +import { getCommonHeaders } from "../../../../services/AuthenticationProvider"; + +export async function getNL2SiteData(aibEndpoint: string, aibToken: string, userPrompt: string, sessionId: string, telemetry: ITelemetry) { + const requestBody = { + "crossGeoOptions": { + "enableCrossGeoCall": true + }, + "question": userPrompt, + "context": { + "sessionId": sessionId, + "scenario": NL2SITE_SCENARIO, + "subScenario": NL2SITE_GENERATE_NEW_SITE, + // "shouldCheckBlockList": false, //TODO: Check if this is needed + "version": "V1", + "information": { + "minPages": 7, + "maxPages": 7 + } + } + }; + + const requestInit: RequestInit = { + method: "POST", + headers: getCommonHeaders(aibToken), + body: JSON.stringify(requestBody) + }; + + try { + const response = await fetch(aibEndpoint, requestInit); + if (!response.ok) { + throw new Error(`${response.statusText} - ${response.status}`); + } + + const responseBody = await response.json(); + + if (responseBody && responseBody.additionalData[0]?.website) { + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_NL2SITE_REQUEST_SUCCESS, {sessionId: sessionId}); + return responseBody.additionalData[0].website; // Contains the pages, siteName & site description + } else { + throw new Error(NL2SITE_INVALID_RESPONSE); + } + } catch (error) { + telemetry.sendTelemetryErrorEvent(VSCODE_EXTENSION_NL2SITE_REQUEST_FAILED, { error: (error as Error)?.message }); + return null; + } +}