Skip to content

Commit 1cc3a91

Browse files
committed
Updates AI quota exceeded UX with role-based messaging
Shows different messages depending on whether the user can purchase credits (owner/admin/billing) or needs to contact their org admin. Adds a direct link to the credit add-on purchase page and tracks user interactions via telemetry. (gitkraken/vscode-gitlens-private#87, #5298)
1 parent 326b078 commit 1cc3a91

3 files changed

Lines changed: 80 additions & 7 deletions

File tree

docs/telemetry-events.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ routed its hook events here — so don't treat every event as a dropped IPC sign
182182
}
183183
```
184184

185+
### ai/credits/addOnClicked
186+
187+
> Sent when the user clicks "Get More Credits" on the weekly AI usage-limit notification
188+
189+
```typescript
190+
{
191+
'organization.role': 'owner' | 'admin' | 'billing' | 'user'
192+
}
193+
```
194+
195+
### ai/credits/addOnDismissed
196+
197+
> Sent when the user dismisses the weekly AI usage-limit notification
198+
199+
```typescript
200+
{
201+
'organization.role': 'owner' | 'admin' | 'billing' | 'user'
202+
}
203+
```
204+
185205
### ai/enabled
186206

187207
> Sent when AI is enabled

src/constants.telemetry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AIActionType } from '@gitlens/ai/models/model.js';
33
import type { GitContributionTiers } from '@gitlens/git/models/contributor.js';
44
import type { Flatten } from '@gitlens/utils/object.js';
55
import type { Config, GraphBranchesVisibility, GraphConfig } from './config.js';
6+
import type { OrganizationRole } from './plus/gk/models/organization.js';
67
import type { GlCommands, GlCommandsDeprecated } from './constants.commands.js';
78
import type { IntegrationIds, SupportedCloudIntegrationIds } from './constants.integrations.js';
89
import type { WalkthroughSteps } from './constants.js';
@@ -88,6 +89,11 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
8889
/** Sent when a user provides feedback (rating and optional details) for an AI feature */
8990
'ai/feedback': AIFeedbackEvent;
9091

92+
/** Sent when the user clicks "Get More Credits" on the weekly AI usage-limit notification */
93+
'ai/credits/addOnClicked': AICreditsNotificationEvent;
94+
/** Sent when the user dismisses the weekly AI usage-limit notification */
95+
'ai/credits/addOnDismissed': AICreditsNotificationEvent;
96+
9197
/** Sent when user dismisses the AI All Access banner */
9298
'aiAllAccess/bannerDismissed': void;
9399

@@ -700,6 +706,10 @@ export interface AIFeedbackEvent extends AIEventDataBase {
700706
'unhelpful.custom'?: string;
701707
}
702708

709+
interface AICreditsNotificationEvent {
710+
'organization.role': OrganizationRole | undefined;
711+
}
712+
703713
export interface CLIInstallStartedEvent {
704714
source?: Sources;
705715
autoInstall: boolean;

src/plus/ai/aiProviderService.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import { configuration } from '../../system/-webview/configuration.js';
6767
import { getContext } from '../../system/-webview/context.js';
6868
import { loadChunk } from '../../system/-webview/loadChunk.js';
6969
import type { Storage } from '../../system/-webview/storage.js';
70+
import { openUrl } from '../../system/-webview/vscode/uris.js';
7071
import type { Serialized } from '../../system/serialize.js';
7172
import type { ServerConnection } from '../gk/serverConnection.js';
7273
import { ensureFeatureAccess } from '../gk/utils/-webview/acount.utils.js';
@@ -1461,14 +1462,56 @@ export class AIProviderService implements AIService, Disposable {
14611462
return undefined;
14621463
}
14631464
case AIErrorReason.UserQuotaExceeded: {
1464-
const increaseLimit: MessageItem = { title: 'Increase Limit' };
1465-
const result = await window.showErrorMessage(
1466-
"Your request could not be completed because you've reached the weekly AI usage limit for your current plan. Upgrade to unlock more AI-powered actions.",
1467-
increaseLimit,
1468-
);
1465+
const sub = await this.container.subscription.getSubscription();
1466+
const role = sub.activeOrganization?.role;
1467+
const canPurchase =
1468+
role == null || role === 'owner' || role === 'admin' || role === 'billing';
1469+
1470+
if (canPurchase) {
1471+
const getMoreCredits: MessageItem = {
1472+
title: 'Get More Credits',
1473+
};
1474+
const dismiss: MessageItem = {
1475+
title: 'Dismiss',
1476+
isCloseAffordance: true,
1477+
};
1478+
const result = await window.showErrorMessage(
1479+
"Your request could not be completed because you've reached the weekly usage included in your plan. Purchase additional AI credits to keep using GitKraken AI.",
1480+
getMoreCredits,
1481+
dismiss,
1482+
);
14691483

1470-
if (result === increaseLimit) {
1471-
void this.container.subscription.manageSubscription(source);
1484+
if (result === getMoreCredits) {
1485+
this.container.telemetry.sendEvent(
1486+
'ai/credits/addOnClicked',
1487+
{ 'organization.role': role },
1488+
source,
1489+
);
1490+
void openUrl(
1491+
await this.container.urls.getGkDevUrl('subscription/credit-add-on'),
1492+
);
1493+
} else {
1494+
this.container.telemetry.sendEvent(
1495+
'ai/credits/addOnDismissed',
1496+
{ 'organization.role': role },
1497+
source,
1498+
);
1499+
}
1500+
} else {
1501+
const ok: MessageItem = {
1502+
title: 'OK',
1503+
isCloseAffordance: true,
1504+
};
1505+
await window.showErrorMessage(
1506+
"Your request could not be completed because you've reached the weekly usage included in your plan. Contact your organization admin or owner to request more AI credits.",
1507+
ok,
1508+
);
1509+
1510+
this.container.telemetry.sendEvent(
1511+
'ai/credits/addOnDismissed',
1512+
{ 'organization.role': role },
1513+
source,
1514+
);
14721515
}
14731516

14741517
if (options?.throwAIErrors) throw error;

0 commit comments

Comments
 (0)