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

feat: troubleshoot UI #12851

Merged
merged 7 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
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
47 changes: 43 additions & 4 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 { 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 @@
workspaceUri?.fsPath &&
isTestToolEnabledProject(workspaceUri.fsPath);

const shouldRecommendTeamsAgent = featureFlagManager.getBooleanValue(
CoreFeatureFlags.ChatParticipantUIEntries
);
const troubleshootErrorWithTeamsAgentButton = {
title: "Troubleshoot error with @teamsapp", // Localize
run: async () => {
await commands.executeCommand(

Check warning on line 52 in packages/vscode-extension/src/error/common.ts

View check run for this annotation

Codecov / codecov/patch

packages/vscode-extension/src/error/common.ts#L51-L52

Added lines #L51 - L52 were not covered by tests
"fx-extension.teamsAgentTroubleshootError",
TelemetryTriggerFrom.Notification,
e
);
},
};

if (recommendTestTool) {
const recommendTestToolMessage = openTestToolMessage();
const recommendTestToolDisplayMessage = openTestToolDisplayMessage();
Expand All @@ -58,7 +81,14 @@
VsCodeLogInstance.error(`code:${errorCode}, message: ${e.message}\n Help link: ${e.helpLink}`);
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
VsCodeLogInstance.debug(`Call stack: ${e.stack || e.innerError?.stack || ""}`);
const buttons = recommendTestTool ? [runTestTool, help] : [help];
const buttons = recommendTestTool
? shouldRecommendTeamsAgent
? []
: [runTestTool, help]
: [help];
if (shouldRecommendTeamsAgent) {
buttons.push(troubleshootErrorWithTeamsAgentButton);
yuqizhou77 marked this conversation as resolved.
Show resolved Hide resolved
}
const button = await window.showErrorMessage(
`[${errorCode}]: ${notificationMessage}`,
...buttons
Expand Down Expand Up @@ -95,6 +125,9 @@
const buttons = recommendTestTool
? [runTestTool, issue, similarIssues]
: [issue, similarIssues];
if (shouldRecommendTeamsAgent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the UI effect of showing 3/4 buttons?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If at most four buttons. user can hover on the button to view full text.
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK thanks! Also, I'd suggest discussing with PM/UX to decide the button number and the primary button to achieve better effect because the main purpose is to increase entry point to troubleshooting.

Copy link
Contributor Author

@yuqizhou77 yuqizhou77 Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just left a comment to UX about button number :) Thanks for your suggestion.
The troubleshoot button has been discussed that should not be the primary one.

buttons.push(troubleshootErrorWithTeamsAgentButton);
}
const button = await window.showErrorMessage(
`[${errorCode}]: ${notificationMessage}`,
...buttons
Expand All @@ -104,7 +137,13 @@
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;
}
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
Loading