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(vscode): show status of rate limited #3635

Merged
merged 17 commits into from
Dec 31, 2024
Merged
22 changes: 22 additions & 0 deletions clients/tabby-agent/src/http/tabbyApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
isUnauthorizedError,
isCanceledError,
isTimeoutError,
isRateLimitExceededError,
} from "../utils/error";
import { RequestStats } from "./statistics";

Expand All @@ -48,6 +49,7 @@ export class TabbyApiClient extends EventEmitter {

private readonly completionRequestStats = new RequestStats();
private completionResponseIssue: "highTimeoutRate" | "slowResponseTime" | undefined = undefined;
private rateLimitExceeded: boolean = false;

private connectionErrorMessage: string | undefined = undefined;
private serverHealth: TabbyApiComponents["schemas"]["HealthState"] | undefined = undefined;
Expand Down Expand Up @@ -176,6 +178,14 @@ export class TabbyApiClient extends EventEmitter {
}
}

private updateIsRateLimitExceeded(isRateLimitExceeded: boolean) {
if (this.rateLimitExceeded != isRateLimitExceeded) {
this.logger.debug(`updateIsRateLimitExceeded: ${isRateLimitExceeded}`);
this.rateLimitExceeded = isRateLimitExceeded;
this.emit("isRateLimitExceededUpdated", isRateLimitExceeded);
}
}

getCompletionRequestStats(): RequestStats {
return this.completionRequestStats;
}
Expand Down Expand Up @@ -214,6 +224,10 @@ export class TabbyApiClient extends EventEmitter {
return !!this.completionResponseIssue;
}

isRateLimitExceeded(): boolean {
return this.rateLimitExceeded;
}

getServerHealth(): TabbyApiComponents["schemas"]["HealthState"] | undefined {
return this.serverHealth;
}
Expand Down Expand Up @@ -370,6 +384,7 @@ export class TabbyApiClient extends EventEmitter {
}
this.logger.trace(`Completion response data: [${requestId}]`, response.data);
statsData.latency = performance.now() - requestStartedAt;
this.updateIsRateLimitExceeded(false);
return response.data;
} catch (error) {
this.updateIsFetchingCompletion(false);
Expand All @@ -382,17 +397,24 @@ export class TabbyApiClient extends EventEmitter {
} else if (isUnauthorizedError(error)) {
this.logger.debug(`Completion request failed due to unauthorized. [${requestId}]`);
statsData.notAvailable = true;
this.updateIsRateLimitExceeded(false);
this.connect(); // schedule a reconnection
} else if (isRateLimitExceededError(error)) {
this.logger.debug(`Completion request failed due to rate limiting. [${requestId}]`);
statsData.notAvailable = true;
this.updateIsRateLimitExceeded(true);
} else {
this.logger.error(`Completion request failed. [${requestId}]`, error);
statsData.notAvailable = true;
this.updateIsRateLimitExceeded(false);
this.connect(); // schedule a reconnection
}
throw error; // rethrow error
} finally {
if (!statsData.notAvailable) {
stats?.addRequestStatsEntry(statsData);
}

if (!statsData.notAvailable && !statsData.canceled) {
this.completionRequestStats.add(statsData.latency);
const statsResult = this.completionRequestStats.stats();
Expand Down
3 changes: 2 additions & 1 deletion clients/tabby-agent/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,8 @@ export type StatusInfo = {
| "readyForAutoTrigger"
| "readyForManualTrigger"
| "fetching"
| "completionResponseSlow";
| "completionResponseSlow"
| "rateLimitExceeded";
tooltip?: string;
/**
* The health information of the server if available.
Expand Down
13 changes: 12 additions & 1 deletion clients/tabby-agent/src/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export class StatusProvider extends EventEmitter implements Feature {
this.tabbyApiClient.on("hasCompletionResponseTimeIssueUpdated", async () => {
this.notify();
});
this.tabbyApiClient.on("isRateLimitExceededUpdated", async () => {
this.notify();
});

this.configurations.on(
"clientProvidedConfigUpdated",
Expand Down Expand Up @@ -203,7 +206,12 @@ export class StatusProvider extends EventEmitter implements Feature {
case "ready":
{
const ignored = this.dataStore.data.statusIgnoredIssues ?? [];
if (this.tabbyApiClient.hasCompletionResponseTimeIssue() && !ignored.includes("completionResponseSlow")) {
if (this.tabbyApiClient.isRateLimitExceeded()) {
statusInfo = { status: "rateLimitExceeded" };
} else if (
this.tabbyApiClient.hasCompletionResponseTimeIssue() &&
!ignored.includes("completionResponseSlow")
) {
statusInfo = { status: "completionResponseSlow" };
} else if (this.tabbyApiClient.isFetchingCompletion()) {
statusInfo = { status: "fetching" };
Expand Down Expand Up @@ -264,6 +272,9 @@ export class StatusProvider extends EventEmitter implements Feature {
case "completionResponseSlow":
statusInfo.tooltip = "Tabby: Slow Completion Response Detected";
break;
case "rateLimitExceeded":
statusInfo.tooltip = "Tabby: Too Many Requests";
break;
default:
break;
}
Expand Down
4 changes: 4 additions & 0 deletions clients/tabby-agent/src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export function isUnauthorizedError(error: any) {
return error instanceof HttpError && [401, 403].includes(error.status);
}

export function isRateLimitExceededError(error: any) {
return error instanceof HttpError && error.status === 429;
}

export function errorToString(error: Error) {
let message = error.message || error.toString();
if (error.cause instanceof Error) {
Expand Down
3 changes: 2 additions & 1 deletion clients/vscode/src/StatusBarItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export class StatusBarItem {
this.setTooltip(statusInfo.tooltip);
break;
}
case "completionResponseSlow": {
case "completionResponseSlow":
case "rateLimitExceeded": {
this.setColorWarning();
this.setIcon(iconWarning);
this.setTooltip(statusInfo.tooltip);
Expand Down
7 changes: 7 additions & 0 deletions clients/vscode/src/commands/commandPalette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ export class CommandPalette {
},
};
}
case "rateLimitExceeded": {
return {
label: `${STATUS_PREFIX}Too Many Requests`,
description: "Request limit exceeded",
command: "tabby.outputPanel.focus",
};
}
default: {
return {
label: `${STATUS_PREFIX}Unknown Status`,
Expand Down
Loading