forked from microsoft/copilot-for-eclipse
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQuotaActions.java
More file actions
115 lines (105 loc) · 5.15 KB
/
Copy pathQuotaActions.java
File metadata and controls
115 lines (105 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package com.microsoft.copilot.eclipse.ui.chat;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.microsoft.copilot.eclipse.core.lsp.protocol.quota.CopilotPlan;
import com.microsoft.copilot.eclipse.ui.UiConstants;
import com.microsoft.copilot.eclipse.ui.i18n.Messages;
/**
* Plan-driven call-to-action list shown when the user's quota is approaching or exceeded. Shared by the
* {@link StaticBanner}-based quota notification (rendered via {@code ActionBar#createQuotaWarningBanner})
* and the inline {@link WarnWidget} rendered under a chat turn on a 402 error response, so both surfaces
* stay in sync when a new plan or call-to-action is added.
*/
public final class QuotaActions {
/**
* Single quota call-to-action.
*
* @param label visible button or link label
* @param tooltip tooltip text used by the inline {@link WarnWidget}; ignored by {@link StaticBanner}
* @param url target URL opened on activation
* @param primary {@code true} when this action should be visually emphasised (e.g. {@code btn-primary}
* styling on a push button); ignored by {@link StaticBanner}, which renders all actions as links
*/
public record QuotaAction(String label, String tooltip, String url, boolean primary) {
}
private QuotaActions() {
}
/**
* Returns the ordered list of {@link QuotaAction}s appropriate for the supplied plan.
*
* <p>Non-upgrade actions are derived from the plan:
* <ul>
* <li>{@code free} → none</li>
* <li>{@code individual}, {@code individual_pro}, {@code individual_max} →
* "Enable Additional Usage" / "Increase Budget"</li>
* <li>{@code business}, {@code enterprise} → none</li>
* </ul>
*
* <p>The "Upgrade Plan" action is then appended when the user is eligible. When
* {@code canUpgradePlan} is non-{@code null} it takes precedence over the plan-based default;
* otherwise the plan default is used (eligible for {@code free}, {@code individual},
* {@code individual_pro}). When the upgrade action is the only entry it is rendered as primary;
* when it follows another primary action it is rendered as secondary so the surfaces stay
* visually consistent.
*
* <p>The "Enable Additional Usage" label is replaced with "Increase Budget" when
* {@code overageEnabled} is {@code true}, matching the IntelliJ quota dialog wording.
*
* @param plan the user's Copilot plan, or {@code null} when unknown
* @param overageEnabled {@code true} when additional paid usage is already enabled for the user
* @param canUpgradePlan whether the user can upgrade their Copilot plan, or {@code null} when the
* language server did not supply this field
* @return an immutable, possibly empty list; never {@code null}
*/
public static List<QuotaAction> forPlan(CopilotPlan plan, boolean overageEnabled, Boolean canUpgradePlan) {
if (plan == null) {
return List.of();
}
QuotaAction upgradePrimary = new QuotaAction(Messages.menu_quota_upgradePlan,
Messages.chat_noQuotaView_updatePlanButton_Tooltip,
UiConstants.COPILOT_UPGRADE_PLAN_URL, true);
QuotaAction upgradeSecondary = new QuotaAction(Messages.menu_quota_upgradePlan,
Messages.chat_noQuotaView_updatePlanButton_Tooltip,
UiConstants.COPILOT_UPGRADE_PLAN_URL, false);
String overageLabel = overageEnabled ? Messages.menu_quota_increaseBudget
: Messages.menu_quota_enableAdditionalUsage;
QuotaAction manageOverage = new QuotaAction(overageLabel,
Messages.chat_noQuotaView_enableAdditionalUsageButton_tooltip,
UiConstants.MANAGE_COPILOT_OVERAGE_URL, true);
boolean hasOverage = switch (plan) {
case individual, individual_pro, individual_max -> true;
case free, business, enterprise -> false;
};
boolean showUpgrade = canUpgradePlan != null ? canUpgradePlan : switch (plan) {
case free, individual, individual_pro -> true;
case individual_max, business, enterprise -> false;
};
if (hasOverage && showUpgrade) {
return List.of(manageOverage, upgradeSecondary);
}
if (hasOverage) {
return List.of(manageOverage);
}
if (showUpgrade) {
return List.of(upgradePrimary);
}
return List.of();
}
/**
* Returns {@code true} when an error response represents a Bring-Your-Own-Key (BYOK) quota-exceeded
* condition, i.e. a {@code 402} from the language server that also carries a non-blank model
* provider name. BYOK usage is governed by the customer's provider account rather than the user's
* Copilot plan, so callers should suppress the plan-driven
* {@link #forPlan(CopilotPlan, boolean, Boolean)} actions and substitute the BYOK-specific
* message in this case.
*
* @param code the error code from the language server
* @param modelProviderName the BYOK model-provider name from the server payload, or {@code null}
* @return {@code true} when the error should be rendered as a BYOK quota notice
*/
public static boolean isByokQuotaExceeded(int code, String modelProviderName) {
return code == 402 && StringUtils.isNotBlank(modelProviderName);
}
}