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

User/amitjoshi/GitHub copilot auth handling #941

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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: 17 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@
"onDebug",
"onFileSystem:powerplatform-vfs"
],
"extensionDependencies": [
"github.copilot-chat"
],
"enabledApiProposals": [
"chatParticipant",
"chatVariableResolver",
"languageModels"
],
"capabilities": {
"untrustedWorkspaces": {
"supported": "limited",
Expand All @@ -112,6 +120,14 @@
}
},
"contributes": {
"chatParticipants": [
{
"id": "powerpages",
"name": "powerpages",
"description": "Power Pages Copilot",
"isSticky": true
}
],
"problemMatchers": [
{
"name": "pcf-scripts-build",
Expand Down Expand Up @@ -1124,4 +1140,4 @@
"bufferutil": "^4.0.6",
"utf-8-validate": "^5.0.9"
}
}
}
2 changes: 1 addition & 1 deletion src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export async function activate(
oneDSLoggerWrapper.getLogger().traceError(exceptionError.name, exceptionError.message, exceptionError, { eventName: 'VscodeDesktopUsage' });
}
// Init OrgChangeNotifier instance
OrgChangeNotifier.createOrgChangeNotifierInstance(pacTerminal.getWrapper());
OrgChangeNotifier.createOrgChangeNotifierInstance(pacTerminal.getWrapper(), _context);

_telemetry.sendTelemetryEvent("PowerPagesWebsiteYmlExists"); // Capture's PowerPages Users
oneDSLoggerWrapper.getLogger().traceInfo("PowerPagesWebsiteYmlExists");
Expand Down
5 changes: 4 additions & 1 deletion src/client/lib/PacActivityBarUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AuthTreeView } from './AuthPanelView';
import { EnvAndSolutionTreeView } from './EnvAndSolutionTreeView';
import { PowerPagesCopilot } from '../../common/copilot/PowerPagesCopilot';
import { ITelemetry } from '../telemetry/ITelemetry';
import { PowerPagesChatParticipant } from '../../common/chat-participants/powerpages/PowerPagesChatParticipant';

export function RegisterPanels(pacWrapper: PacWrapper, context: vscode.ExtensionContext, telemetry: ITelemetry): vscode.Disposable[] {
const authPanel = new AuthTreeView(() => pacWrapper.authList(), pacWrapper);
Expand All @@ -20,11 +21,13 @@ export function RegisterPanels(pacWrapper: PacWrapper, context: vscode.Extension

const copilotPanel = new PowerPagesCopilot(context.extensionUri, context, telemetry, pacWrapper);

const powerPagesChatParticipant = PowerPagesChatParticipant.getInstance(context, telemetry, pacWrapper);

vscode.window.registerWebviewViewProvider('powerpages.copilot', copilotPanel, {
webviewOptions: {
retainContextWhenHidden: true,
},
});

return [authPanel, envAndSolutionPanel, copilotPanel];
return [authPanel, envAndSolutionPanel, copilotPanel, powerPagesChatParticipant];
}
11 changes: 7 additions & 4 deletions src/common/OrgChangeNotifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ export class OrgChangeNotifier {
private _pacWrapper: PacWrapper | undefined;
private _orgDetails: ActiveOrgOutput | undefined;
private static _orgChangeNotifierObj: OrgChangeNotifier | undefined;
private extensionContext: vscode.ExtensionContext;

private constructor(pacWrapper: PacWrapper) {
private constructor(pacWrapper: PacWrapper, extensionContext: vscode.ExtensionContext) {
this._pacWrapper = pacWrapper;
this.activeOrgDetails();
if (this._pacWrapper) {
this.setupFileWatcher();
}

this.extensionContext = extensionContext;
}

public static createOrgChangeNotifierInstance(pacWrapper: PacWrapper) {
public static createOrgChangeNotifierInstance(pacWrapper: PacWrapper, extensionContext: vscode.ExtensionContext) {
if (!OrgChangeNotifier._orgChangeNotifierObj) {
OrgChangeNotifier._orgChangeNotifierObj = new OrgChangeNotifier(pacWrapper);
OrgChangeNotifier._orgChangeNotifierObj = new OrgChangeNotifier(pacWrapper, extensionContext);
}
return OrgChangeNotifier._orgChangeNotifierObj;
}
Expand All @@ -53,4 +56,4 @@ export class OrgChangeNotifier {
orgChangeErrorEventEmitter.fire();
}
}
}
}
10 changes: 10 additions & 0 deletions src/common/chat-participants/ChatParticipantUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import * as vscode from 'vscode';

export function createChatParticipant(participantId: string, handler: vscode.ChatRequestHandler): vscode.ChatParticipant {
return vscode.chat.createChatParticipant(participantId, handler);
}
195 changes: 195 additions & 0 deletions src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import * as vscode from 'vscode';
import { createChatParticipant } from '../ChatParticipantUtils';
import { IPowerPagesChatResult } from './PowerPagesChatParticipantTypes';
import { ITelemetry } from '../../../client/telemetry/ITelemetry';
import TelemetryReporter from '@vscode/extension-telemetry';
import { getIntelligenceEndpoint } from '../../ArtemisService';
import { sendApiRequest } from '../../copilot/IntelligenceApiService';
import { PacWrapper } from '../../../client/pac/PacWrapper';
import { PAC_SUCCESS } from '../../copilot/constants';
import { createAuthProfileExp } from '../../Utils';
import { intelligenceAPIAuthentication } from '../../AuthenticationProvider';
import { ActiveOrgOutput } from '../../../client/pac/PacTypes';
import { orgChangeErrorEvent, orgChangeEvent } from '../../OrgChangeNotifier';

export interface OrgDetails {
orgID: string;
orgUrl: string;
}

export class PowerPagesChatParticipant {
private static instance : PowerPagesChatParticipant | null = null;
private chatParticipant: vscode.ChatParticipant;
private telemetry: ITelemetry;
private extensionContext: vscode.ExtensionContext;
private readonly _pacWrapper?: PacWrapper;
private isOrgDetailsInitialized = false;
private readonly _disposables: vscode.Disposable[] = [];
private cachedEndpoint: { intelligenceEndpoint: string, geoName: string } | null = null;

private orgID: string | undefined;
private orgUrl: string | undefined;

private constructor(context: vscode.ExtensionContext, telemetry: ITelemetry | TelemetryReporter, pacWrapper?: PacWrapper) {

this.chatParticipant = createChatParticipant('powerpages', this.handler);

//TODO: Check the icon image
this.chatParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'chat-participants', 'powerpages', 'assets', 'copilot.png');

this.telemetry = telemetry;

this.extensionContext = context;

this._pacWrapper = pacWrapper;

this._disposables.push(orgChangeEvent(async (orgDetails: ActiveOrgOutput) => {
await this.handleOrgChangeSuccess(orgDetails);
}));

this._disposables.push(orgChangeErrorEvent(async () => {
await createAuthProfileExp(this._pacWrapper);
}));

}

public static getInstance(context: vscode.ExtensionContext, telemetry: ITelemetry | TelemetryReporter, pacWrapper?: PacWrapper) {
if (!PowerPagesChatParticipant.instance) {
PowerPagesChatParticipant.instance = new PowerPagesChatParticipant(context, telemetry, pacWrapper);
}

return PowerPagesChatParticipant.instance;
}

public dispose() {
this.chatParticipant.dispose();
}

private handler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest,
_context: vscode.ChatContext,
stream: vscode.ChatResponseStream,
_token: vscode.CancellationToken
): Promise<IPowerPagesChatResult> => {
// Handle chat requests here

stream.progress('Working on it...')

await this.intializeOrgDetails();

if (!this.orgID) {
// TODO: Auth Create Experience
await createAuthProfileExp(this._pacWrapper);
return {
metadata: {
command: ''
}
};
}

const intelligenceApiAuthResponse = await intelligenceAPIAuthentication(this.telemetry, '', this.orgID, true);

if (!intelligenceApiAuthResponse) {

//TODO: Handle auth error and provide a way to re-authenticate

return {
metadata: {
command: '',
}
};
}

const intelligenceApiToken = intelligenceApiAuthResponse.accessToken;

const { intelligenceEndpoint, geoName } = await this.getEndpoint(this.orgID, this.telemetry);

if (!intelligenceEndpoint || !geoName) {
//TODO: Handle error

return {
metadata: {
command: ''
}
};
}

const userPrompt = request.prompt;

//TODO: Handle form and list scenarios

if (!userPrompt) {

//TODO: Show start message

return {
metadata: {
command: ''
}
};
}

// export async function sendApiRequest(userPrompt: UserPrompt[], activeFileParams: IActiveFileParams, orgID: string, apiToken: string, sessionID: string, entityName: string, entityColumns: string[], telemetry: ITelemetry, aibEndpoint: string | null, geoName: string | null) {}
const llmResponse = await sendApiRequest([{displayText: userPrompt, code: ''}], {dataverseEntity:'', entityField: '', fieldType: ''}, this.orgID, intelligenceApiToken, '', '', [], this.telemetry, intelligenceEndpoint, geoName);

stream.markdown(llmResponse[0].displayText);

stream.markdown('\n```typescript\n' + llmResponse[0].code + '\n```');
// TODO: Handle authentication and org change

console.log(_token)

return {
metadata: {
command: ''
}
};

};

private async intializeOrgDetails(): Promise<void> {

if(this.isOrgDetailsInitialized) {
return;
}

this.isOrgDetailsInitialized = true;

const orgDetails:OrgDetails | undefined = this.extensionContext.globalState.get('orgDetails');

if(orgDetails) {
this.orgID = orgDetails.orgID;
this.orgUrl = orgDetails.orgUrl;
} else {
if (this._pacWrapper) {
const pacActiveOrg = await this._pacWrapper.activeOrg();
if (pacActiveOrg && pacActiveOrg.Status === PAC_SUCCESS) {
this.handleOrgChangeSuccess(pacActiveOrg.Results);
} else {
await createAuthProfileExp(this._pacWrapper);
}
}
}
}

private async handleOrgChangeSuccess(orgDetails: ActiveOrgOutput) {
this.orgID = orgDetails.OrgId;
this.orgUrl = orgDetails.OrgUrl

this.extensionContext.globalState.update('orgDetails', {orgID: this.orgID, orgUrl: this.orgUrl});

//TODO: Handle AIB GEOs
}

async getEndpoint(orgID: string, telemetry: ITelemetry) {
if (!this.cachedEndpoint) {
this.cachedEndpoint = await getIntelligenceEndpoint(orgID, telemetry, '') as { intelligenceEndpoint: string; geoName: string };
}
return this.cachedEndpoint;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import * as vscode from 'vscode';

export interface IPowerPagesChatResult extends vscode.ChatResult {
metadata: {
command: string;
}
}
3 changes: 3 additions & 0 deletions src/common/chat-participants/powerpages/assets/copilot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading