feat(vscode): Add dont-show-again for warnings. Add online help links. (#821)
parent
fcfa13136a
commit
6ed94a7951
|
|
@ -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)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue