Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users/amitjoshi/gpt tokenizer integration #734

Merged
merged 29 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b0b83f0
getting info about selected code
Sep 27, 2023
59cb3b5
showing label for selected lines of code
Sep 29, 2023
a48f1c8
add flag to disable feature
Sep 29, 2023
8d96a0c
updated responsiveness
Sep 29, 2023
321030b
code formatting fix
Sep 29, 2023
9c3dcd7
Merge branch 'main' into users/amitjoshi/labelForSelectedCode
tyaginidhi Sep 29, 2023
49a6de4
removed log statement
Sep 29, 2023
fbc324b
Merge branch 'users/amitjoshi/labelForSelectedCode' of https://github…
Sep 29, 2023
d9bef02
handles empty selections
Sep 29, 2023
662cc05
passing and showing user selected code to copilot
Sep 29, 2023
4fa0dad
setting fixed vertical height for user code
Oct 4, 2023
4317781
Merge branch 'main' into users/amitjoshi/showingSelectedCodeInCopilot…
Oct 4, 2023
fd28310
removed redundant code
Oct 4, 2023
8b9ce0d
added comment
Oct 4, 2023
7455aa9
Merge branch 'main' into users/amitjoshi/showingSelectedCodeInCopilot…
Oct 5, 2023
2d78054
added const for explainCode msg
Oct 5, 2023
b8774a6
sending localized string to copilot webview
Oct 6, 2023
de8b869
gpt-tokenizer integration
Oct 6, 2023
07fc8d1
user prompt with selected code and 'explain' check
Oct 12, 2023
2b5efe2
showing context cmd only when copilot registered
Oct 13, 2023
810176c
update handling of empty snippet
Oct 13, 2023
288b11c
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Oct 16, 2023
51da821
updated when clause
Oct 16, 2023
6ba0b1f
moved code to skip in a const
Oct 17, 2023
02cca4e
Merge branch 'main' into users/amitjoshi/gptTokenizerIntegration
amitjoshi438 Oct 17, 2023
df0149c
Added telemetry
Oct 17, 2023
5a7f080
Merge branch 'users/amitjoshi/gptTokenizerIntegration' of https://git…
Oct 17, 2023
70752a2
updated strings
Oct 17, 2023
d4879c5
user initial fix
Oct 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@
{
"submenu": "microsoft-powerapps-portals.powerpages-copilot",
"group": "0_powerpages-copilot",
"when": "never"
"when": "(powerpages.copilot.isVisible) && ((!virtualWorkspace && powerpages.websiteYmlExists && config.powerPlatform.experimental.copilotEnabled) || (isWeb && config.powerPlatform.experimental.enableWebCopilot))"
}
],
"microsoft-powerapps-portals.powerpages-copilot": [
Expand Down Expand Up @@ -1033,6 +1033,7 @@
"command-exists": "^1.2.9",
"find-process": "^1.4.7",
"glob": "^7.1.7",
"gpt-tokenizer": "^2.1.1",
"liquidjs": "^10.2.0",
"n-readlines": "^1.0.1",
"puppeteer-core": "^14.4.1",
Expand Down
8 changes: 4 additions & 4 deletions src/common/copilot/IntelligenceApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import fetch, { RequestInit } from "node-fetch";
import { INAPPROPRIATE_CONTENT, INPUT_CONTENT_FILTERED, INVALID_INFERENCE_INPUT, InvalidResponse, MalaciousScenerioResponse, NetworkError, PROMPT_LIMIT_EXCEEDED, PromptLimitExceededResponse, RELEVANCY_CHECK_FAILED, RateLimitingResponse, UnauthorizedResponse } from "./constants";
import { INAPPROPRIATE_CONTENT, INPUT_CONTENT_FILTERED, INVALID_INFERENCE_INPUT, InvalidResponse, MalaciousScenerioResponse, NetworkError, PROMPT_LIMIT_EXCEEDED, PromptLimitExceededResponse, RELEVANCY_CHECK_FAILED, RateLimitingResponse, UnauthorizedResponse, UserPrompt } from "./constants";
import { IActiveFileParams } from "./model";
import { sendTelemetryEvent } from "./telemetry/copilotTelemetry";
import { ITelemetry } from "../../client/telemetry/ITelemetry";
Expand All @@ -15,14 +15,14 @@ import { EXTENSION_NAME } from "../../client/constants";
const clientType = EXTENSION_NAME + '-' + getExtensionType();
const clientVersion = getExtensionVersion();

export async function sendApiRequest(userPrompt: string, activeFileParams: IActiveFileParams, orgID: string, apiToken: string, sessionID: string, entityName: string, entityColumns: string[], telemetry: ITelemetry, aibEndpoint: string | null) {
export async function sendApiRequest(userPrompt: UserPrompt[], activeFileParams: IActiveFileParams, orgID: string, apiToken: string, sessionID: string, entityName: string, entityColumns: string[], telemetry: ITelemetry, aibEndpoint: string | null) {

if (!aibEndpoint) {
return NetworkError;
}

const requestBody = {
"question": userPrompt,
"question": userPrompt[0].displayText,
"top": 1,
"context": {
"sessionId": sessionID,
Expand All @@ -33,7 +33,7 @@ export async function sendApiRequest(userPrompt: string, activeFileParams: IActi
"dataverseEntity": activeFileParams.dataverseEntity,
"entityField": activeFileParams.entityField,
"fieldType": activeFileParams.fieldType,
"activeFileContent": '', //TODO: Add active file content (selected code)
"activeFileContent": userPrompt[0].code, //Active file content (selected code)
"targetEntity": entityName,
"targetColumns": entityColumns,
"clientType": clientType,
Expand Down
27 changes: 20 additions & 7 deletions src/common/copilot/PowerPagesCopilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import { dataverseAuthentication, intelligenceAPIAuthentication } from "../../we
import { v4 as uuidv4 } from 'uuid'
import { PacWrapper } from "../../client/pac/PacWrapper";
import { ITelemetry } from "../../client/telemetry/ITelemetry";
import { AUTH_CREATE_FAILED, AUTH_CREATE_MESSAGE, AuthProfileNotFound, COPILOT_UNAVAILABLE, CopilotDisclaimer, CopilotStylePathSegments, DataverseEntityNameMap, EXPLAIN_CODE, EntityFieldMap, FieldTypeMap, PAC_SUCCESS, SELECTED_CODE_INFO_ENABLED, WebViewMessage, sendIconSvg } from "./constants";
import { AUTH_CREATE_FAILED, AUTH_CREATE_MESSAGE, AuthProfileNotFound, COPILOT_UNAVAILABLE, CopilotDisclaimer, CopilotStylePathSegments, DataverseEntityNameMap, EXPLAIN_CODE, EntityFieldMap, FieldTypeMap, PAC_SUCCESS, SELECTED_CODE_INFO, SELECTED_CODE_INFO_ENABLED, UserPrompt, WebViewMessage, sendIconSvg } from "./constants";
import { IActiveFileParams, IActiveFileData, IOrgInfo } from './model';
import { escapeDollarSign, getLastThreePartsOfFileName, getNonce, getSelectedCode, getSelectedCodeLineRange, getUserName, openWalkthrough, showConnectedOrgMessage, showInputBoxAndGetOrgUrl, showProgressWithNotification } from "../Utils";
import { CESUserFeedback } from "./user-feedback/CESSurvey";
import { GetAuthProfileWatchPattern } from "../../client/lib/AuthPanelView";
import { ActiveOrgOutput } from "../../client/pac/PacTypes";
import { CopilotWalkthroughEvent, CopilotCopyCodeToClipboardEvent, CopilotInsertCodeToEditorEvent, CopilotLoadedEvent, CopilotOrgChangedEvent, CopilotUserFeedbackThumbsDownEvent, CopilotUserFeedbackThumbsUpEvent, CopilotUserPromptedEvent, CopilotCodeLineCountEvent, CopilotClearChatEvent, CopilotNotAvailable } from "./telemetry/telemetryConstants";
import { CopilotWalkthroughEvent, CopilotCopyCodeToClipboardEvent, CopilotInsertCodeToEditorEvent, CopilotLoadedEvent, CopilotOrgChangedEvent, CopilotUserFeedbackThumbsDownEvent, CopilotUserFeedbackThumbsUpEvent, CopilotUserPromptedEvent, CopilotCodeLineCountEvent, CopilotClearChatEvent, CopilotNotAvailable, CopilotExplainCode, CopilotExplainCodeSize } from "./telemetry/telemetryConstants";
import { sendTelemetryEvent } from "./telemetry/copilotTelemetry";
import { INTELLIGENCE_SCOPE_DEFAULT, PROVIDER_ID } from "../../web/client/common/constants";
import { getIntelligenceEndpoint } from "../ArtemisService";
import TelemetryReporter from "@vscode/extension-telemetry";
import { getEntityColumns, getEntityName } from "./dataverseMetadata";
import { COPILOT_STRINGS } from "./assets/copilotStrings";
import { isWithinTokenLimit, encode } from "gpt-tokenizer";

let intelligenceApiToken: string;
let userID: string; // Populated from PAC or intelligence API
Expand Down Expand Up @@ -79,18 +80,26 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
const selectedCodeLineRange = getSelectedCodeLineRange(editor);
if(commandType === EXPLAIN_CODE && selectedCode.length === 0) {
// Show a message if the selection is empty and don't send the message to webview
vscode.window.showInformationMessage(vscode.l10n.t('Selection is empty!'));
vscode.window.showInformationMessage(vscode.l10n.t('Selection is empty.'));
return;
}
this.sendMessageToWebview({ type: commandType, value: { start: selectedCodeLineRange.start, end: selectedCodeLineRange.end, selectedCode: selectedCode } });
const withinTokenLimit = isWithinTokenLimit(selectedCode, 1000);
if(commandType === EXPLAIN_CODE) {
const tokenSize = encode(selectedCode).length;
sendTelemetryEvent(this.telemetry, { eventName: CopilotExplainCodeSize, copilotSessionId: sessionID, orgId: orgID, codeLineCount: String(selectedCodeLineRange.end - selectedCodeLineRange.start), tokenSize: String(tokenSize) });
if(withinTokenLimit === false) {
return;
}
}
this.sendMessageToWebview({ type: commandType, value: { start: selectedCodeLineRange.start, end: selectedCodeLineRange.end, selectedCode: selectedCode, tokenSize: withinTokenLimit } });
};

this._disposables.push(
vscode.window.onDidChangeTextEditorSelection(() => handleSelectionChange("selectedCodeInfo"))
vscode.window.onDidChangeTextEditorSelection(() => handleSelectionChange(SELECTED_CODE_INFO)), vscode.window.onDidChangeActiveTextEditor(() => handleSelectionChange(SELECTED_CODE_INFO))
);

this._disposables.push(
vscode.commands.registerCommand("powerpages.copilot.explain", () => handleSelectionChange(EXPLAIN_CODE))
vscode.commands.registerCommand("powerpages.copilot.explain", () => {sendTelemetryEvent(this.telemetry, { eventName: CopilotExplainCode, copilotSessionId: sessionID, orgId: orgID }); this.show(); handleSelectionChange(EXPLAIN_CODE)})
);
}

Expand Down Expand Up @@ -167,6 +176,10 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {

const pacOutput = await this._pacWrapper?.activeOrg();

if(SELECTED_CODE_INFO_ENABLED){
vscode.commands.executeCommand('setContext', 'powerpages.copilot.isVisible', true);
}

if (pacOutput && pacOutput.Status === PAC_SUCCESS) {
await this.handleOrgChangeSuccess(pacOutput.Results);
} else if (!IS_DESKTOP && orgID && activeOrgUrl) {
Expand Down Expand Up @@ -319,7 +332,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
}
}

private async authenticateAndSendAPIRequest(data: string, activeFileParams: IActiveFileParams, orgID: string, telemetry: ITelemetry) {
private async authenticateAndSendAPIRequest(data: UserPrompt[], activeFileParams: IActiveFileParams, orgID: string, telemetry: ITelemetry) {
return intelligenceAPIAuthentication(telemetry, sessionID, orgID)
.then(async ({ accessToken, user, userId }) => {
intelligenceApiToken = accessToken;
Expand Down
3 changes: 2 additions & 1 deletion src/common/copilot/assets/copilotStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
import vscode from "vscode";

export const COPILOT_STRINGS = {
EXPLAIN_CODE_PROMPT: vscode.l10n.t('Explain the following code:'),
EXPLAIN_CODE_PROMPT: vscode.l10n.t('Explain the following code snippet:'),
LARGE_SELECTION: vscode.l10n.t('Selection is too large. Try making a shorter selection.'),
}
19 changes: 13 additions & 6 deletions src/common/copilot/assets/scripts/copilot.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
const chatMessages = document.getElementById("chat-messages");
const chatInput = document.getElementById("chat-input");
const chatInputComponent = document.getElementById("input-component");
const skipCodes = ["", null, undefined, "violation", "unclear", "explain"];

let userName;
let apiResponseHandler;
Expand All @@ -24,6 +25,7 @@
let copilotStrings = {};



const inputHistory = [];
let currentIndex = -1;

Expand Down Expand Up @@ -79,7 +81,7 @@
return resultDiv;
}

if (responseText[i].code === "" || responseText[i].code === null || responseText[i].code === undefined || responseText[i].code === "violation" || responseText[i].code === "unclear") {
if (skipCodes.includes(responseText[i].code)) {
continue;
}

Expand Down Expand Up @@ -147,7 +149,7 @@
const nameArray = name.split(" ");
const initials = nameArray.map((word) => word.charAt(0));
const truncatedInitials = initials.slice(0, 2);
return truncatedInitials.join("");
return truncatedInitials.join("").toUpperCase();
}


Expand Down Expand Up @@ -455,7 +457,7 @@
break;
}
case "Available": {
if(isCopilotEnabled== false) {
if(isCopilotEnabled === false) {
isCopilotEnabled = true;
chatInputComponent.classList.remove("hide");
chatMessages.innerHTML = "";
Expand All @@ -467,12 +469,17 @@
case "selectedCodeInfo": {
const chatInputLabel = document.getElementById("input-label-id");
selectedCode = message.value.selectedCode;
if (message.value.start == message.value.end && selectedCode.length == 0) {
if (selectedCode.length === 0) {
chatInputLabel.classList.add("hide");
break;
}
chatInputLabel.classList.remove("hide");
chatInputLabel.innerText = `Lines: ${message.value.start + 1} - ${message.value.end + 1} selected`;
if(message.value.tokenSize === false){
chatInputLabel.innerText = copilotStrings.LARGE_SELECTION;
selectedCode = "";
break;
}
chatInputLabel.innerText = `Lines ${message.value.start + 1} - ${message.value.end + 1} selected`;
break;
}
case "explainCode": {
Expand Down Expand Up @@ -520,7 +527,7 @@
chatInput.disabled = true;
saveInputToHistory(input);
apiResponseInProgress = true;
getApiResponse(input + ': ' + selectedCode); //TODO: userPrompt object should be passed
getApiResponse(userPrompt); //TODO: userPrompt object should be passed
chatInput.value = "";
chatInput.focus();
}
Expand Down
6 changes: 6 additions & 0 deletions src/common/copilot/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const PROMPT_LIMIT_EXCEEDED = 'PromptLimitExceeded';
export const INVALID_INFERENCE_INPUT = 'InvalidInferenceInput';
export const COPILOT_NOTIFICATION_DISABLED = 'isCopilotNotificationDisabled'
export const EXPLAIN_CODE = 'explainCode';
export const SELECTED_CODE_INFO = "selectedCodeInfo";
export const SELECTED_CODE_INFO_ENABLED = false;

export type WebViewMessage = {
Expand All @@ -35,6 +36,11 @@ export type WebViewMessage = {
envName?: string;
};

export interface UserPrompt {
displayText: string;
code: string;
}

export const DataverseEntityNameMap = new Map<string, string>([
['webpage', 'adx_webpage'],
['list', 'adx_entitylist'],
Expand Down
1 change: 1 addition & 0 deletions src/common/copilot/telemetry/ITelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export interface IProDevCopilotTelemetryData {
geoName?: string,
aibEndpoint?: string,
orgUrl?: string,
tokenSize?: string
}
3 changes: 2 additions & 1 deletion src/common/copilot/telemetry/copilotTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export function sendTelemetryEvent(telemetry: ITelemetry, telemetryData: IProDev
telemetryDataProperties.FeedbackId = telemetryData.FeedbackId ? telemetryData.FeedbackId : '';
telemetryDataProperties.dataverseEntity = telemetryData.dataverseEntity ? telemetryData.dataverseEntity : '';
telemetryDataProperties.responseStatus = telemetryData.responseStatus ? telemetryData.responseStatus : '';

telemetryDataProperties.tokenSize = telemetryData.tokenSize ? telemetryData.tokenSize : '';

if (telemetryData.error) {
telemetryDataProperties.eventName = telemetryData.eventName;
telemetry.sendTelemetryException(telemetryData.error, telemetryDataProperties, telemetryDataMeasurements);
Expand Down
2 changes: 2 additions & 0 deletions src/common/copilot/telemetry/telemetryConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ export const CopilotNotificationShown = 'CopilotNotificationShown';
export const CopilotNotificationDoNotShowChecked = 'CopilotNotificationDoNotShowChecked';
export const CopilotNotificationDoNotShowUnchecked = 'CopilotNotificationDoNotShowUnchecked';
export const CopilotNotAvailable = 'CopilotNotAvailable';
export const CopilotExplainCode = 'CopilotExplainCode';
export const CopilotExplainCodeSize = 'CopilotExplainCodeSize';