Skip to content

Commit 7374dff

Browse files
Fixes some AI cancellation cases being treated as errors (#4609)
1 parent 84cbb55 commit 7374dff

File tree

4 files changed

+49
-8
lines changed

4 files changed

+49
-8
lines changed

src/plus/ai/aiProviderService.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
AINoRequestDataError,
2626
AuthenticationRequiredError,
2727
CancellationError,
28+
isCancellationError,
2829
} from '../../errors';
2930
import type { AIFeatures } from '../../features';
3031
import { isAdvancedFeature } from '../../features';
@@ -1477,7 +1478,7 @@ export class AIProviderService implements Disposable {
14771478
const model = await this.getModel(undefined, source);
14781479
if (model == null || options?.cancellation?.isCancellationRequested) {
14791480
options?.generating?.cancel();
1480-
return undefined;
1481+
return 'cancelled';
14811482
}
14821483

14831484
const promise = this.sendRequestWithModel(
@@ -1522,7 +1523,7 @@ export class AIProviderService implements Disposable {
15221523
const model = await this.getModel(undefined, source);
15231524
if (model == null || options?.cancellation?.isCancellationRequested) {
15241525
options?.generating?.cancel();
1525-
return undefined;
1526+
return 'cancelled';
15261527
}
15271528

15281529
return this.sendRequestWithModel(
@@ -1618,7 +1619,24 @@ export class AIProviderService implements Disposable {
16181619
return 'cancelled';
16191620
}
16201621

1621-
const apiKey = await this._provider!.getApiKey(false);
1622+
let apiKey: string | undefined;
1623+
try {
1624+
apiKey = await this._provider!.getApiKey(false);
1625+
} catch (ex) {
1626+
if (isCancellationError(ex)) {
1627+
setLogScopeExit(scope, `model: ${model.provider.id}/${model.id}`, 'cancelled: user cancelled');
1628+
this.container.telemetry.sendEvent(
1629+
telementry.key,
1630+
{ ...telementry.data, failed: true, 'failed.reason': 'user-cancelled' },
1631+
source,
1632+
);
1633+
1634+
options?.generating?.cancel();
1635+
return 'cancelled';
1636+
}
1637+
1638+
throw ex;
1639+
}
16221640

16231641
if (cancellation.isCancellationRequested) {
16241642
setLogScopeExit(scope, `model: ${model.provider.id}/${model.id}`, 'cancelled: user cancelled');

src/plus/ai/openAICompatibleProviderBase.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { fetch } from '@env/fetch';
55
import type { Role } from '../../@types/vsls';
66
import type { AIProviders } from '../../constants.ai';
77
import type { Container } from '../../container';
8-
import { AIError, AIErrorReason, CancellationError } from '../../errors';
8+
import { AIError, AIErrorReason, CancellationError, isCancellationError } from '../../errors';
99
import { getLoggableName, Logger } from '../../system/logger';
1010
import { startLogScope } from '../../system/logger.scope';
1111
import type { ServerConnection } from '../gk/serverConnection';
@@ -38,7 +38,14 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
3838
protected abstract readonly config: { keyUrl?: string; keyValidator?: RegExp };
3939

4040
async configured(silent: boolean): Promise<boolean> {
41-
return (await this.getApiKey(silent)) != null;
41+
try {
42+
const apiKey = await this.getApiKey(silent);
43+
return apiKey != null;
44+
} catch (ex) {
45+
if (isCancellationError(ex)) return false;
46+
47+
throw ex;
48+
}
4249
}
4350

4451
async getApiKey(silent: boolean): Promise<string | undefined> {

src/plus/ai/openRouterProvider.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { fetch } from '@env/fetch';
22
import { openRouterProviderDescriptor as provider } from '../../constants.ai';
3+
import { isCancellationError } from '../../errors';
34
import type { AIActionType, AIModel } from './models/model';
45
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
56

@@ -15,7 +16,15 @@ export class OpenRouterProvider extends OpenAICompatibleProviderBase<typeof prov
1516
};
1617

1718
async getModels(): Promise<readonly AIModel<typeof provider.id>[]> {
18-
const apiKey = await this.getApiKey(true);
19+
let apiKey: string | undefined;
20+
try {
21+
apiKey = await this.getApiKey(true);
22+
} catch (ex) {
23+
if (isCancellationError(ex)) return [];
24+
25+
throw ex;
26+
}
27+
1928
if (!apiKey) return [];
2029

2130
const url = 'https://openrouter.ai/api/v1/models';

src/plus/ai/utils/-webview/ai.utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Schemes } from '../../../../constants';
44
import type { AIProviders } from '../../../../constants.ai';
55
import type { Container } from '../../../../container';
66
import type { MarkdownContentMetadata } from '../../../../documents/markdown';
7+
import { CancellationError } from '../../../../errors';
78
import { decodeGitLensRevisionUriAuthority } from '../../../../git/gitUri.authority';
89
import { createDirectiveQuickPickItem, Directive } from '../../../../quickpicks/items/directive';
910
import { configuration } from '../../../../system/-webview/configuration';
@@ -17,8 +18,8 @@ import { ensureAccountQuickPick } from '../../../gk/utils/-webview/acount.utils'
1718
import type { AIResult, AIResultContext } from '../../aiProviderService';
1819
import type { AIActionType, AIModel } from '../../models/model';
1920

20-
export function ensureAccount(container: Container, silent: boolean): Promise<boolean> {
21-
return ensureAccountQuickPick(
21+
export async function ensureAccount(container: Container, silent: boolean): Promise<boolean> {
22+
const result = await ensureAccountQuickPick(
2223
container,
2324
createDirectiveQuickPickItem(Directive.Noop, undefined, {
2425
label: 'Use AI-powered GitLens features like Generate Commit Message, Explain Commit, and more',
@@ -27,6 +28,12 @@ export function ensureAccount(container: Container, silent: boolean): Promise<bo
2728
{ source: 'ai' },
2829
silent,
2930
);
31+
32+
if (!result && !silent) {
33+
throw new CancellationError();
34+
}
35+
36+
return result;
3037
}
3138

3239
export function getActionName(action: AIActionType): string {

0 commit comments

Comments
 (0)