Skip to content

Commit

Permalink
feat: troubleshoot UI (#12851)
Browse files Browse the repository at this point in the history
* feat: command

* feat: selected text troubleshot

* refactor: condition

* feat: troubleshoot error

* test: ut

* test: ut

* refactor: remove unused change
  • Loading branch information
yuqizhou77 authored Dec 5, 2024
1 parent 34dee94 commit 54dd9cf
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 22 deletions.
27 changes: 27 additions & 0 deletions packages/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,13 @@
"group": "inline"
}
],
"editor/context": [
{
"command": "fx-extension.teamsAgentTroubleshootSelectedText",
"group": "navigation",
"when": "(fx-extension.isChatParticipantUIEntriesEnabled && (resourceFilename =~ /.*Teams Toolkit\\.log$/ || (activeOutputChannel =~ /^extension-output-TeamsDevApp\\.ms-teams-vscode-extension.*/ && view == 'workbench.panel.output')))"
}
],
"editor/title/run": [
{
"command": "fx-extension.selectAndDebug",
Expand Down Expand Up @@ -547,6 +554,14 @@
{
"command": "fx-extension.openOfficeDevHelpFeedbackLink",
"when": "false"
},
{
"command": "fx-extension.teamsAgentTroubleshootSelectedText",
"when": "false"
},
{
"command": "fx-extension.teamsAgentTroubleshootError",
"when": "false"
}
]
},
Expand Down Expand Up @@ -907,6 +922,18 @@
"title": "%teamstoolkit.commandsTreeViewProvider.syncManifest%",
"category": "Teams",
"enablement": "fx-extension.isSyncManifestEnabled"
},
{
"command": "fx-extension.teamsAgentTroubleshootSelectedText",
"title": "Troubleshoot selection with @teamsapp",
"enablement": "editor.hasSelection && fx-extension.isChatParticipantUIEntriesEnabled",
"category": "Teams"
},
{
"command": "fx-extension.teamsAgentTroubleshootError",
"title": "Troubleshoot error with @teamsapp",
"enablement": "fx-extension.isChatParticipantUIEntriesEnabled",
"category": "Teams"
}
],
"taskDefinitions": [
Expand Down
41 changes: 38 additions & 3 deletions packages/vscode-extension/src/error/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// Licensed under the MIT license.

import { UserError, SystemError, FxError, Result, err, ok } from "@microsoft/teamsfx-api";
import { isUserCancelError, ConcurrentError } from "@microsoft/teamsfx-core";
import {
isUserCancelError,
ConcurrentError,
featureFlagManager,
FeatureFlags as CoreFeatureFlags,
} from "@microsoft/teamsfx-core";
import { Uri, commands, window } from "vscode";
import {
RecommendedOperations,
Expand All @@ -14,7 +19,11 @@ import { ExtTelemetry } from "../telemetry/extTelemetry";
import { anonymizeFilePaths } from "../utils/fileSystemUtils";
import { localize } from "../utils/localizeUtils";
import { isTestToolEnabledProject } from "../utils/projectChecker";
import { TelemetryEvent, TelemetryProperty } from "../telemetry/extTelemetryEvents";
import {
TelemetryEvent,
TelemetryProperty,
TelemetryTriggerFrom,
} from "../telemetry/extTelemetryEvents";
import VsCodeLogInstance from "../commonlib/log";
import { ExtensionSource, ExtensionErrors } from "./error";

Expand All @@ -34,6 +43,20 @@ export async function showError(e: UserError | SystemError) {
workspaceUri?.fsPath &&
isTestToolEnabledProject(workspaceUri.fsPath);

const shouldRecommendTeamsAgent = featureFlagManager.getBooleanValue(
CoreFeatureFlags.ChatParticipantUIEntries
);
const troubleshootErrorWithTeamsAgentButton = {
title: "Troubleshoot error with @teamsapp", // Localize
run: async () => {
await commands.executeCommand(
"fx-extension.teamsAgentTroubleshootError",
TelemetryTriggerFrom.Notification,
e
);
},
};

if (recommendTestTool) {
const recommendTestToolMessage = openTestToolMessage();
const recommendTestToolDisplayMessage = openTestToolDisplayMessage();
Expand All @@ -59,6 +82,9 @@ export async function showError(e: UserError | SystemError) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
VsCodeLogInstance.debug(`Call stack: ${e.stack || e.innerError?.stack || ""}`);
const buttons = recommendTestTool ? [runTestTool, help] : [help];
if (shouldRecommendTeamsAgent) {
buttons.push(troubleshootErrorWithTeamsAgentButton);
}
const button = await window.showErrorMessage(
`[${errorCode}]: ${notificationMessage}`,
...buttons
Expand Down Expand Up @@ -95,6 +121,9 @@ export async function showError(e: UserError | SystemError) {
const buttons = recommendTestTool
? [runTestTool, issue, similarIssues]
: [issue, similarIssues];
if (shouldRecommendTeamsAgent) {
buttons.push(troubleshootErrorWithTeamsAgentButton);
}
const button = await window.showErrorMessage(
`[${errorCode}]: ${notificationMessage}`,
...buttons
Expand All @@ -104,7 +133,13 @@ export async function showError(e: UserError | SystemError) {
if (!(e instanceof ConcurrentError)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
VsCodeLogInstance.debug(`Call stack: ${e.stack || e.innerError?.stack || ""}`);
const buttons = recommendTestTool ? [runTestTool] : [];
const buttons: {
title: string;
run: () => void;
}[] = recommendTestTool ? [runTestTool] : [];
if (shouldRecommendTeamsAgent) {
buttons.push(troubleshootErrorWithTeamsAgentButton);
}
const button = await window.showErrorMessage(
`[${errorCode}]: ${notificationMessage}`,
...buttons
Expand Down
12 changes: 12 additions & 0 deletions packages/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,18 @@ function registerActivateCommands(context: vscode.ExtensionContext) {
Correlator.run(copilotChatHandlers.invokeTeamsAgent, args)
);
context.subscriptions.push(invokeTeamsAgent);

const troubleshootSelectedText = vscode.commands.registerCommand(
"fx-extension.teamsAgentTroubleshootSelectedText",
(...args) => Correlator.run(copilotChatHandlers.troubleshootSelectedText, args)
);
context.subscriptions.push(troubleshootSelectedText);

const troubleshootError = vscode.commands.registerCommand(
"fx-extension.teamsAgentTroubleshootError",
(...args) => Correlator.run(copilotChatHandlers.troubleshootError, args)
);
context.subscriptions.push(troubleshootError);
}

/**
Expand Down
151 changes: 132 additions & 19 deletions packages/vscode-extension/src/handlers/copilotChatHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as util from "util";
import * as vscode from "vscode";

import { FxError, Result, SystemError, err, ok } from "@microsoft/teamsfx-api";
import { FxError, Result, SystemError, UserError, err, ok } from "@microsoft/teamsfx-api";
import { assembleError, globalStateGet, globalStateUpdate } from "@microsoft/teamsfx-core";
import { UserCancelError, sleep } from "@microsoft/vscode-ui";
import VsCodeLogInstance from "../commonlib/log";
Expand All @@ -24,6 +24,12 @@ import { VS_CODE_UI } from "../qm/vsc_ui";
const githubCopilotChatExtensionId = "github.copilot-chat";
const teamsAgentLink = "https://aka.ms/install-teamsapp";

enum errorNames {
NoActiveTextEditor = "NoActiveTextEditor",
CannotVerifyGithubCopilotChat = "CannotVerifyGithubCopilotChat",
openCopilotError = "openCopilotError",
}

function githubCopilotInstalled(): boolean {
const extension = vscode.extensions.getExtension(githubCopilotChatExtensionId);
return !!extension;
Expand All @@ -42,7 +48,7 @@ async function openGithubCopilotChat(query: string): Promise<Result<null, FxErro
} catch (e) {
const error = new SystemError(
eventName,
"openCopilotError",
errorNames.openCopilotError,
util.format(localize("teamstoolkit.handlers.chatTeamsAgentError", query)),
util.format(localize("teamstoolkit.handlers.chatTeamsAgentError", query))
);
Expand Down Expand Up @@ -147,18 +153,11 @@ export async function handleInstallTeamsAgentSelection(
}
}

export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxError>> {
const eventName = TelemetryEvent.InvokeTeamsAgent;
const triggerFromProperty = getTriggerFromProperty(args);
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.InvokeTeamsAgentStart, triggerFromProperty);

const query =
triggerFromProperty["trigger-from"] === TelemetryTriggerFrom.TreeView ||
triggerFromProperty["trigger-from"] === TelemetryTriggerFrom.CommandPalette
? "@teamsapp Use this GitHub Copilot extension to ask questions about Teams app and agent development."
: "@teamsapp Write your own query message to find relevant templates or samples to build your Teams app and agent as per your description. E.g. @teamsapp create an AI assistant bot that can complete common tasks.";
let res: Result<null, FxError>;

async function invoke(
query: string,
eventName: string,
triggerFromProperty: { [key: string]: TelemetryTriggerFrom }
): Promise<Result<null, FxError>> {
const skipRemindInstallTeamsAgent = await globalStateGet(
GlobalKey.DoNotRemindInstallTeamsAgent,
false
Expand All @@ -181,7 +180,7 @@ export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxErr
util.format(localize("teamstoolkit.handlers.installAgent.output"), teamsAgentLink)
);
showOutputChannelHandler();
res = await openGithubCopilotChat(query);
return await openGithubCopilotChat(query);
} else {
VsCodeLogInstance.info(
util.format(
Expand Down Expand Up @@ -211,11 +210,11 @@ export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxErr

if (verifyExtensionInstalled) {
await sleep(2000); // wait for extension activation
res = await openGithubCopilotChat(query);
return await openGithubCopilotChat(query);
} else {
const error = new SystemError(
eventName,
"CannotVerifyGithubCopilotChat",
errorNames.CannotVerifyGithubCopilotChat,
util.format(
localize("teamstoolkit.handlers.verifyCopilotExtensionError", InstallCopilotChatLink)
),
Expand All @@ -224,12 +223,31 @@ export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxErr
)
);
VsCodeLogInstance.error(error.message);
res = err(error);
return err(error);
}
} else {
res = installRes;
return installRes;
}
}
}

/**
* Invokes GitHub Copilot Chat for creating new app or development questions.
* @param args args
* @returns Result
*/
export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxError>> {
const eventName = TelemetryEvent.InvokeTeamsAgent;
const triggerFromProperty = getTriggerFromProperty(args);
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.InvokeTeamsAgentStart, triggerFromProperty);

const query =
triggerFromProperty["trigger-from"] === TelemetryTriggerFrom.TreeView ||
triggerFromProperty["trigger-from"] === TelemetryTriggerFrom.CommandPalette
? "@teamsapp Use this GitHub Copilot extension to ask questions about Teams app and agent development."
: "@teamsapp Write your own query message to find relevant templates or samples to build your Teams app and agent as per your description. E.g. @teamsapp create an AI assistant bot that can complete common tasks.";
const res = await invoke(query, eventName, triggerFromProperty);

if (res.isErr()) {
ExtTelemetry.sendTelemetryErrorEvent(eventName, res.error, triggerFromProperty);
} else {
Expand All @@ -240,3 +258,98 @@ export async function invokeTeamsAgent(args?: any[]): Promise<Result<null, FxErr
}
return res;
}

/**
* Invokes teams agent for troubleshooting based on selected text.
* @param args
* @returns Result
*/
export async function troubleshootSelectedText(args?: any[]): Promise<Result<null, FxError>> {
const eventName = TelemetryEvent.TroubleshootSelectedText;
const triggerFromProperty = getTriggerFromProperty([TelemetryTriggerFrom.EditorContextMenu]);
ExtTelemetry.sendTelemetryEvent(
TelemetryEvent.TroubleshootSelectedTextStart,
triggerFromProperty
);

const editor = vscode.window.activeTextEditor;
let selectedText = "";
if (editor) {
const selection = editor.selection;
selectedText = editor.document.getText(selection);
} else {
return err(
new UserError(
eventName,
errorNames.NoActiveTextEditor,
"No active text. Please select some text for troubleshooting." // TODO: localize.
)
);
}

const query = `@teamsapp I'm encountering the following error in my code.
\`\`\`
{
Error message: ${selectedText}
}
\`\`\`
Can you help me diagnose the issue and suggest possible solutions?
`;
const res = await invoke(query, eventName, triggerFromProperty);

if (res.isErr()) {
ExtTelemetry.sendTelemetryErrorEvent(eventName, res.error, triggerFromProperty);
} else {
ExtTelemetry.sendTelemetryEvent(eventName, {
[TelemetryProperty.Success]: TelemetrySuccess.Yes,
...triggerFromProperty,
});
}
return res;
}

/**
* Invokes teams agent for troubleshooting current error.
* @param args
* @returns Result
*/
export async function troubleshootError(args?: any[]): Promise<Result<null, FxError>> {
const eventName = TelemetryEvent.TroubleshootErrorFromNotification;
if (!args || args.length !== 2) {
// should never happen
return ok(null);
}

const currentError = args[1] as FxError;
const errorCode = `${currentError.source}.${currentError.name}`;
const triggerFromProperty = getTriggerFromProperty(args);
const telemtryProperties = {
...triggerFromProperty,
[TelemetryProperty.ErrorCode]: errorCode,
};
ExtTelemetry.sendTelemetryEvent(
TelemetryEvent.TroubleshootErrorFromNotificationStart,
telemtryProperties
);

const query = `@teamsapp I'm encountering the following error in Teams Toolkit.
\`\`\`
{
Error code: ${errorCode}
Error message: ${currentError.message}
}
\`\`\`
Can you help me diagnose the issue and suggest possible solutions?
`;
const res = await invoke(query, eventName, triggerFromProperty);

if (res.isErr()) {
ExtTelemetry.sendTelemetryErrorEvent(eventName, res.error, telemtryProperties);
} else {
ExtTelemetry.sendTelemetryEvent(eventName, {
[TelemetryProperty.Success]: TelemetrySuccess.Yes,
...telemtryProperties,
});
}
return res;
}
6 changes: 6 additions & 0 deletions packages/vscode-extension/src/telemetry/extTelemetryEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,13 @@ export enum TelemetryEvent {

FindSimilarIssues = "find-similar-issues",

// Teams Github Copilot UI
InvokeTeamsAgentStart = "invoke-teams-agent-start",
InvokeTeamsAgent = "invoke-teams-agent",
TroubleshootSelectedTextStart = "troubleshoot-selected-text-start",
TroubleshootSelectedText = "troubleshoot-selected-text",
TroubleshootErrorFromNotificationStart = "troubleshoot-error-from-notification-start",
TroubleshootErrorFromNotification = "troubleshoot-error-from-notification",

// Copilot Chat
CopilotChatStart = "copilot-chat-start",
Expand Down Expand Up @@ -459,6 +464,7 @@ export enum TelemetryTriggerFrom {
SampleDetailPage = "SampleDetailPage",
CopilotChat = "CopilotChat",
CreateAppQuestionFlow = "CreateAppQuestionFlow",
EditorContextMenu = "EditorContextMenu",
Other = "Other",
Auto = "Auto",
Unknow = "Unknow",
Expand Down
2 changes: 2 additions & 0 deletions packages/vscode-extension/src/utils/telemetryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export function getTriggerFromProperty(args?: any[]) {
return { [TelemetryProperty.TriggerFrom]: TelemetryTriggerFrom.Other };
case TelemetryTriggerFrom.CreateAppQuestionFlow:
return { [TelemetryProperty.TriggerFrom]: TelemetryTriggerFrom.CreateAppQuestionFlow };
case TelemetryTriggerFrom.EditorContextMenu:
return { [TelemetryProperty.TriggerFrom]: TelemetryTriggerFrom.EditorContextMenu };
default:
return { [TelemetryProperty.TriggerFrom]: TelemetryTriggerFrom.Unknow };
}
Expand Down
Loading

0 comments on commit 54dd9cf

Please sign in to comment.