feat(vscode): Add dont-show-again for warnings. Add online help links. (#821)

release-fix-intellij-update-support-version-range
Zhiming Ma 2023-11-18 12:33:59 +08:00 committed by GitHub
parent fcfa13136a
commit 6ed94a7951
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 194 additions and 54 deletions

View File

@ -57,6 +57,14 @@
{
"command": "tabby.gettingStarted",
"title": "Tabby: Getting Started"
},
{
"command": "tabby.openOnlineHelp",
"title": "Tabby: Online Help"
},
{
"command": "tabby.notifications.resetMuted",
"title": "Tabby: Reset notifications marked as \"Don't Show Again\""
}
],
"menus": {
@ -147,7 +155,7 @@
"tabby.usage.anonymousUsageTracking": {
"type": "boolean",
"default": false,
"markdownDescription": "**Disable anonymous usage tracking** \nTabby collects aggregated anonymous usage data and sends it to the Tabby team to help improve our products. \nYour code, generated completions, or any identifying information is never tracked or transmitted. \nFor more details on data collection, please check our [online documentation](https://tabby.tabbyml.com/docs/extensions/configuration#usage-collection)."
"markdownDescription": "**Disable anonymous usage tracking** \nTabby collects aggregated anonymous usage data and sends it to the Tabby team to help improve our products. \nYour code, generated completions, or any identifying information is never tracked or transmitted. \nFor more details on data collection, please check our [online documentation](https://tabby.tabbyml.com/docs/extensions/configurations#usage-collection)."
}
}
},

View File

@ -14,20 +14,12 @@ import { CompletionRequest, CompletionResponse, LogEventRequest } from "tabby-ag
import { agent } from "./agent";
export class TabbyCompletionProvider extends EventEmitter implements InlineCompletionItemProvider {
static instance: TabbyCompletionProvider;
static getInstance(): TabbyCompletionProvider {
if (!TabbyCompletionProvider.instance) {
TabbyCompletionProvider.instance = new TabbyCompletionProvider();
}
return TabbyCompletionProvider.instance;
}
private triggerMode: "automatic" | "manual" | "disabled" = "automatic";
private onGoingRequestAbortController: AbortController | null = null;
private loading: boolean = false;
private latestCompletions: CompletionResponse | null = null;
private constructor() {
public constructor() {
super();
this.updateConfiguration();
workspace.onDidChangeConfiguration((event) => {

View File

@ -1,4 +1,4 @@
import { StatusBarAlignment, ThemeColor, window } from "vscode";
import { StatusBarAlignment, ThemeColor, ExtensionContext, window } from "vscode";
import { createMachine, interpret } from "@xstate/fsm";
import type { StatusChangedEvent, AuthRequiredEvent, IssuesUpdatedEvent } from "tabby-agent";
import { agent } from "./agent";
@ -20,13 +20,26 @@ const backgroundColorWarning = new ThemeColor("statusBarItem.warningBackground")
export class TabbyStatusBarItem {
private item = window.createStatusBarItem(StatusBarAlignment.Right);
private extensionContext: ExtensionContext;
private completionProvider: TabbyCompletionProvider;
private completionResponseWarningShown = false;
private subStatusForReady = [
{
target: "issuesExist",
cond: () => agent().getIssues().length > 0,
cond: () => {
let issues = agent().getIssues();
if (
this.extensionContext.globalState
.get<string[]>("notifications.muted", [])
.includes("completionResponseTimeIssues")
) {
issues = issues.filter(
(issue) => issue !== "highCompletionTimeoutRate" && issue !== "slowCompletionResponseTime",
);
}
return issues.length > 0;
},
},
{
target: "automatic",
@ -126,18 +139,20 @@ export class TabbyStatusBarItem {
private fsmService = interpret(this.fsm);
constructor(completionProvider: TabbyCompletionProvider) {
constructor(context: ExtensionContext, completionProvider: TabbyCompletionProvider) {
this.extensionContext = context;
this.completionProvider = completionProvider;
this.fsmService.start();
this.fsmService.send(agent().getStatus());
this.item.show();
this.completionProvider.on("triggerModeUpdated", () => {
this.fsmService.send(agent().getStatus());
this.refresh();
});
this.completionProvider.on("loadingStatusUpdated", () => {
this.fsmService.send(agent().getStatus());
this.refresh();
});
agent().on("statusChanged", (event: StatusChangedEvent) => {
console.debug("Tabby agent statusChanged", { event });
this.fsmService.send(event.status);
@ -158,12 +173,17 @@ export class TabbyStatusBarItem {
agent().on("issuesUpdated", (event: IssuesUpdatedEvent) => {
console.debug("Tabby agent issuesUpdated", { event });
this.fsmService.send(agent().getStatus());
const showCompletionResponseWarnings =
!this.completionResponseWarningShown &&
!this.extensionContext.globalState
.get<string[]>("notifications.muted", [])
.includes("completionResponseTimeIssues");
if (event.issues.includes("connectionFailed")) {
notifications.showInformationWhenDisconnected();
} else if (!this.completionResponseWarningShown && event.issues.includes("highCompletionTimeoutRate")) {
} else if (showCompletionResponseWarnings && event.issues.includes("highCompletionTimeoutRate")) {
this.completionResponseWarningShown = true;
notifications.showInformationWhenHighCompletionTimeoutRate();
} else if (!this.completionResponseWarningShown && event.issues.includes("slowCompletionResponseTime")) {
} else if (showCompletionResponseWarnings && event.issues.includes("slowCompletionResponseTime")) {
this.completionResponseWarningShown = true;
notifications.showInformationWhenSlowCompletionResponseTime();
}
@ -174,6 +194,10 @@ export class TabbyStatusBarItem {
return this.item;
}
public refresh() {
this.fsmService.send(agent().getStatus());
}
private toInitializing() {
this.item.color = colorNormal;
this.item.backgroundColor = backgroundColorNormal;

View File

@ -3,6 +3,8 @@ import {
InputBoxValidationSeverity,
ProgressLocation,
Uri,
ThemeIcon,
ExtensionContext,
workspace,
window,
env,
@ -12,6 +14,7 @@ import { strict as assert } from "assert";
import { agent } from "./agent";
import { notifications } from "./notifications";
import { TabbyCompletionProvider } from "./TabbyCompletionProvider";
import { TabbyStatusBarItem } from "./TabbyStatusBarItem";
const configTarget = ConfigurationTarget.Global;
@ -175,24 +178,114 @@ const acceptInlineCompletion: Command = {
},
};
const acceptInlineCompletionNextWord: Command = {
command: "tabby.inlineCompletion.acceptNextWord",
const acceptInlineCompletionNextWord = (completionProvider: TabbyCompletionProvider): Command => {
return {
command: "tabby.inlineCompletion.acceptNextWord",
callback: () => {
completionProvider.postEvent("accept_word");
commands.executeCommand("editor.action.inlineSuggest.acceptNextWord");
},
};
};
const acceptInlineCompletionNextLine = (completionProvider: TabbyCompletionProvider): Command => {
return {
command: "tabby.inlineCompletion.acceptNextLine",
callback: () => {
completionProvider.postEvent("accept_line");
// FIXME: this command move cursor to next line, but we want to move cursor to the end of current line
commands.executeCommand("editor.action.inlineSuggest.acceptNextLine");
},
};
};
const openOnlineHelp: Command = {
command: "tabby.openOnlineHelp",
callback: () => {
TabbyCompletionProvider.getInstance().postEvent("accept_word");
commands.executeCommand("editor.action.inlineSuggest.acceptNextWord");
window
.showQuickPick([
{
label: "Online Documentation",
iconPath: new ThemeIcon("book"),
alwaysShow: true,
},
{
label: "Model Registry",
description: "Explore more recommend models from Tabby's model registry",
iconPath: new ThemeIcon("library"),
alwaysShow: true,
},
{
label: "Tabby Slack Community",
description: "Join Tabby's Slack community to get help or feed back",
iconPath: new ThemeIcon("comment-discussion"),
alwaysShow: true,
},
{
label: "Tabby GitHub Repository",
description: "View the source code for Tabby, and open issues",
iconPath: new ThemeIcon("github"),
alwaysShow: true,
},
])
.then((selection) => {
if (selection) {
switch (selection.label) {
case "Online Documentation":
env.openExternal(Uri.parse("https://tabby.tabbyml.com/"));
break;
case "Model Registry":
env.openExternal(Uri.parse("https://tabby.tabbyml.com/docs/models/"));
break;
case "Tabby Slack Community":
env.openExternal(
Uri.parse("https://join.slack.com/t/tabbycommunity/shared_invite/zt-1xeiddizp-bciR2RtFTaJ37RBxr8VxpA"),
);
break;
case "Tabby GitHub Repository":
env.openExternal(Uri.parse("https://github.com/tabbyml/tabby"));
break;
}
}
});
},
};
const acceptInlineCompletionNextLine: Command = {
command: "tabby.inlineCompletion.acceptNextLine",
callback: () => {
TabbyCompletionProvider.getInstance().postEvent("accept_line");
// FIXME: this command move cursor to next line, but we want to move cursor to the end of current line
commands.executeCommand("editor.action.inlineSuggest.acceptNextLine");
},
const muteNotifications = (context: ExtensionContext, statusBarItem: TabbyStatusBarItem): Command => {
return {
command: "tabby.notifications.mute",
callback: (type: string) => {
const notifications = context.globalState.get<string[]>("notifications.muted", []);
notifications.push(type);
context.globalState.update("notifications.muted", notifications);
statusBarItem.refresh();
},
};
};
export const tabbyCommands = () =>
const resetMutedNotifications = (context: ExtensionContext, statusBarItem: TabbyStatusBarItem): Command => {
return {
command: "tabby.notifications.resetMuted",
callback: (type?: string) => {
const notifications = context.globalState.get<string[]>("notifications.muted", []);
if (type) {
context.globalState.update(
"notifications.muted",
notifications.filter((t) => t !== type),
);
} else {
context.globalState.update("notifications.muted", []);
}
statusBarItem.refresh();
},
};
};
export const tabbyCommands = (
context: ExtensionContext,
completionProvider: TabbyCompletionProvider,
statusBarItem: TabbyStatusBarItem,
) =>
[
toggleInlineCompletionTriggerMode,
setApiEndpoint,
@ -204,6 +297,9 @@ export const tabbyCommands = () =>
applyCallback,
triggerInlineCompletion,
acceptInlineCompletion,
acceptInlineCompletionNextWord,
acceptInlineCompletionNextLine,
acceptInlineCompletionNextWord(completionProvider),
acceptInlineCompletionNextLine(completionProvider),
openOnlineHelp,
muteNotifications(context, statusBarItem),
resetMutedNotifications(context, statusBarItem),
].map((command) => commands.registerCommand(command.command, command.callback, command.thisArg));

View File

@ -11,12 +11,12 @@ import { TabbyStatusBarItem } from "./TabbyStatusBarItem";
export async function activate(context: ExtensionContext) {
console.debug("Activating Tabby extension", new Date());
await createAgentInstance(context);
const completionProvider = TabbyCompletionProvider.getInstance();
const statusBarItem = new TabbyStatusBarItem(completionProvider);
const completionProvider = new TabbyCompletionProvider();
const statusBarItem = new TabbyStatusBarItem(context, completionProvider);
context.subscriptions.push(
languages.registerInlineCompletionItemProvider({ pattern: "**" }, completionProvider),
statusBarItem.register(),
...tabbyCommands(),
...tabbyCommands(context, completionProvider, statusBarItem),
);
}

View File

@ -1,4 +1,4 @@
import { commands, window, workspace, env, ConfigurationTarget, Uri } from "vscode";
import { commands, window, workspace, ConfigurationTarget } from "vscode";
import type {
HighCompletionTimeoutRateIssue,
SlowCompletionResponseTimeIssue,
@ -100,9 +100,13 @@ function showInformationWhenDisconnected(modal: boolean = false) {
detail: message,
},
"Settings",
"Online Help...",
)
.then((selection) => {
switch (selection) {
case "Online Help...":
commands.executeCommand("tabby.openOnlineHelp");
break;
case "Settings":
commands.executeCommand("tabby.openSettings");
break;
@ -165,7 +169,7 @@ function getHelpMessageForCompletionResponseTimeIssue() {
helpMessageForRunningLargeModelOnCPU +=
`Your Tabby server is running model ${serverHealthState?.model} on CPU. ` +
"This model may be performing poorly due to its large parameter size, please consider trying smaller models or switch to GPU. " +
"You can find a list of supported models in the model directory.\n";
"You can find a list of recommend models in the online documentation.\n";
}
let commonHelpMessage = "";
const host = new URL(agent().getConfig().server.endpoint).host;
@ -174,7 +178,7 @@ function getHelpMessageForCompletionResponseTimeIssue() {
serverHealthState?.model ?? ""
} may be performing poorly due to its large parameter size. `;
commonHelpMessage +=
"Please consider trying smaller models. You can find a list of supported models in the model directory.\n";
"Please consider trying smaller models. You can find a list of recommend models in the online documentation.\n";
}
if (!(host.startsWith("localhost") || host.startsWith("127.0.0.1"))) {
commonHelpMessage += " - A poor network connection. Please check your network and proxy settings.\n";
@ -212,18 +216,22 @@ function showInformationWhenSlowCompletionResponseTime(modal: boolean = false) {
modal: true,
detail: statsMessage + getHelpMessageForCompletionResponseTimeIssue(),
},
"Model Directory",
"Online Help...",
"Don't Show Again",
)
.then((selection) => {
switch (selection) {
case "Model Directory":
env.openExternal(Uri.parse("https://tabby.tabbyml.com/docs/models/"));
case "Online Help...":
commands.executeCommand("tabby.openOnlineHelp");
break;
case "Don't Show Again":
commands.executeCommand("tabby.notifications.mute", "completionResponseTimeIssues");
break;
}
});
} else {
window
.showWarningMessage("Completion requests appear to take too much time.", "Detail", "Settings")
.showWarningMessage("Completion requests appear to take too much time.", "Detail", "Settings", "Don't Show Again")
.then((selection) => {
switch (selection) {
case "Detail":
@ -232,6 +240,9 @@ function showInformationWhenSlowCompletionResponseTime(modal: boolean = false) {
case "Settings":
commands.executeCommand("tabby.openSettings");
break;
case "Don't Show Again":
commands.executeCommand("tabby.notifications.mute", "completionResponseTimeIssues");
break;
}
});
}
@ -252,26 +263,35 @@ function showInformationWhenHighCompletionTimeoutRate(modal: boolean = false) {
modal: true,
detail: statsMessage + getHelpMessageForCompletionResponseTimeIssue(),
},
"Model Directory",
"Online Help...",
"Don't Show Again",
)
.then((selection) => {
switch (selection) {
case "Model Directory":
env.openExternal(Uri.parse("https://tabby.tabbyml.com/docs/models/"));
case "Online Help...":
commands.executeCommand("tabby.openOnlineHelp");
break;
case "Don't Show Again":
commands.executeCommand("tabby.notifications.mute", "completionResponseTimeIssues");
break;
}
});
} else {
window.showWarningMessage("Most completion requests timed out.", "Detail", "Settings").then((selection) => {
switch (selection) {
case "Detail":
showInformationWhenHighCompletionTimeoutRate(true);
break;
case "Settings":
commands.executeCommand("tabby.openSettings");
break;
}
});
window
.showWarningMessage("Most completion requests timed out.", "Detail", "Settings", "Don't Show Again")
.then((selection) => {
switch (selection) {
case "Detail":
showInformationWhenHighCompletionTimeoutRate(true);
break;
case "Settings":
commands.executeCommand("tabby.openSettings");
break;
case "Don't Show Again":
commands.executeCommand("tabby.notifications.mute", "completionResponseTimeIssues");
break;
}
});
}
}