Skip to content

Commit abf4e15

Browse files
committed
wip
1 parent d612404 commit abf4e15

File tree

10 files changed

+121
-92
lines changed

10 files changed

+121
-92
lines changed

chat-client/src/client/mynahUi.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import {
5757
} from './utils'
5858
import { ChatHistory, ChatHistoryList } from './features/history'
5959
import { pairProgrammingModeOff, pairProgrammingModeOn, programmerModeCard } from './texts/pairProgramming'
60-
import { paidTierCard, upgradeQButton } from './texts/paidTier'
60+
import { paidTierSuccessCard, freeTierLimitReachedCard, upgradeQButton } from './texts/paidTier'
6161

6262
export interface InboundChatApi {
6363
addChatResponse(params: ChatResult, tabId: string, isPartialResult: boolean): void
@@ -822,20 +822,39 @@ export const createMynahUi = (
822822
})
823823
}
824824

825-
const updateChat = (params: ChatUpdateParams) => {
826-
if (params.data?.placeholderText === 'upgrade-q') {
827-
const tabId = params.tabId !== 'xxx' ? params.tabId : getOrCreateTabId()!
828-
const upgradeQMode: 'paidtier' | 'freetier' | 'freetier-limit' = (params as any).upgradeQMode
829-
// TODO: only do this for 'freetier-limit'
830-
const needUpgrade = upgradeQMode === 'freetier' || upgradeQMode === 'freetier-limit'
825+
/**
826+
* Adjusts the UI when the user changes to/from free-tier/paid-tier.
827+
* Shows a message if the user reaches free-tier limit.
828+
* Shows a message if the user just upgraded to paid-tier.
829+
*/
830+
const onPaidTierModeChange = (
831+
tabId: string,
832+
mode: 'paidtier' | 'paidtier-success' | 'freetier' | 'freetier-limit'
833+
) => {
834+
if (!['paidtier', 'paidtier-success', 'freetier', 'freetier-limit'].includes(mode)) {
835+
return // invalid mode
836+
}
831837

832-
mynahUi.updateStore(tabId, {
833-
promptInputButtons: needUpgrade ? [upgradeQButton] : [],
834-
promptInputDisabledState: needUpgrade,
835-
})
836-
if (needUpgrade) {
837-
mynahUi.addChatItem(tabId, paidTierCard)
838-
}
838+
tabId = tabId !== '' ? tabId : getOrCreateTabId()!
839+
840+
// Detect if the tab is already showing the "Upgrade Q" calls-to-action.
841+
const didShowLimitReached = mynahUi.getTabData(tabId)?.getStore()?.promptInputButtons?.[0] === upgradeQButton
842+
if (mode === 'freetier-limit' && !didShowLimitReached) {
843+
mynahUi.addChatItem(tabId, freeTierLimitReachedCard)
844+
} else if (mode === 'paidtier-success') {
845+
mynahUi.addChatItem(tabId, paidTierSuccessCard)
846+
}
847+
848+
mynahUi.updateStore(tabId, {
849+
promptInputButtons: mode === 'freetier-limit' ? [upgradeQButton] : [],
850+
promptInputDisabledState: mode === 'freetier-limit',
851+
})
852+
}
853+
854+
const updateChat = (params: ChatUpdateParams) => {
855+
// HACK: Special field sent by `agenticChatController.ts:setPaidTierMode()`.
856+
if ((params as any).paidTierMode) {
857+
onPaidTierModeChange(params.tabId, (params as any).paidTierMode as any)
839858
return
840859
}
841860

chat-client/src/client/tabs/tabFactory.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { disclaimerCard } from '../texts/disclaimer'
1010
import { ChatMessage } from '@aws/language-server-runtimes-types'
1111
import { ChatHistory } from '../features/history'
1212
import { pairProgrammingPromptInput, programmerModeCard } from '../texts/pairProgramming'
13-
import { paidTierCard, upgradeQButton } from '../texts/paidTier'
1413

1514
export type DefaultTabData = MynahUIDataModel
1615

@@ -50,15 +49,13 @@ export class TabFactory {
5049
public getChatItems(
5150
needWelcomeMessages: boolean,
5251
pairProgrammingCardActive: boolean,
53-
// paidTierCardActive: boolean,
5452
chatMessages?: ChatMessage[]
5553
): ChatItem[] {
5654
return [
5755
...(this.bannerMessage ? [this.getBannerMessage() as ChatItem] : []),
5856
...(needWelcomeMessages
5957
? [
6058
...(this.agenticMode && pairProgrammingCardActive ? [programmerModeCard] : []),
61-
// ...(paidTierCardActive ? [paidTierCard] : []),
6259
{
6360
type: ChatItemType.ANSWER,
6461
body: `Hi, I'm Amazon Q. I can answer your software development questions.

chat-client/src/client/texts/paidTier.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
1-
import { ChatItem, ChatItemButton, ChatItemFormItem, ChatItemType } from '@aws/mynah-ui'
1+
import { ChatItem, ChatItemButton, ChatItemFormItem, ChatItemType, TextBasedFormItem } from '@aws/mynah-ui'
22

3-
export const paidTierCard: ChatItem = {
3+
export const freeTierLimitReachedCard: ChatItem = {
44
type: ChatItemType.ANSWER,
55
title: 'FREE TIER LIMIT REACHED',
66
header: {
77
icon: 'q',
88
iconStatus: 'primary',
99
body: 'Upgrade to Amazon Q Pro',
1010
},
11-
messageId: 'paidTierCardId',
11+
messageId: 'freetier-limit',
1212
fullWidth: true,
1313
canBeDismissed: false,
1414
body: 'You have reached the free tier limit. Upgrade to Amazon Q Pro.\n\n[Learn More...](https://aws.amazon.com/q/pricing/)',
1515
}
1616

17-
export const paidTierPromptInput: ChatItemFormItem = {
18-
type: 'switch',
17+
export const paidTierSuccessCard: ChatItem = {
18+
type: ChatItemType.ANSWER,
19+
title: 'UPGRADED TO AMAZON Q PRO',
20+
header: {
21+
icon: 'q',
22+
iconStatus: 'primary',
23+
body: 'Welcome to Amazon Q Pro',
24+
status: {
25+
status: 'success',
26+
icon: 'q',
27+
text: 'Success',
28+
},
29+
},
30+
messageId: 'paidtier-success',
31+
fullWidth: true,
32+
canBeDismissed: true,
33+
body: 'Upgraded to Amazon Q Pro\n\n[Learn More...](https://aws.amazon.com/q/)',
34+
}
35+
36+
export const paidTierPromptInput: TextBasedFormItem = {
37+
placeholder: '111111111111',
38+
type: 'textinput',
1939
id: 'paid-tier',
2040
tooltip: 'Upgrade to Amazon Q Pro',
2141
value: 'true',

server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
PromptInputOptionChangeParams,
3131
TextDocument,
3232
ChatUpdateParams,
33+
MessageType,
3334
} from '@aws/language-server-runtimes/protocol'
3435
import {
3536
ApplyWorkspaceEditParams,
@@ -132,6 +133,7 @@ import { URI } from 'vscode-uri'
132133
import { AgenticChatError, customerFacingErrorCodes, isRequestAbortedError, unactionableErrorCodes } from './errors'
133134
import { CommandCategory } from './tools/executeBash'
134135
import { UserWrittenCodeTracker } from '../../shared/userWrittenCodeTracker'
136+
import { PaidTierMode } from '../paidTier/paidTier'
135137

136138
type ChatHandlers = Omit<
137139
LspHandlers<Chat>,
@@ -163,7 +165,7 @@ export class AgenticChatController implements ChatHandlers {
163165
#userWrittenCodeTracker: UserWrittenCodeTracker | undefined
164166
#toolUseStartTimes: Record<string, number> = {}
165167
#toolUseLatencies: Array<{ toolName: string; toolUseId: string; latency: number }> = []
166-
#freeTierLimit = false
168+
#paidTierMode: PaidTierMode | undefined
167169

168170
/**
169171
* Determines the appropriate message ID for a tool use based on tool type and name
@@ -277,29 +279,47 @@ export class AgenticChatController implements ChatHandlers {
277279
.createSubscriptionToken({
278280
accountId: awsAccountId,
279281
})
282+
280283
if (!r.encodedVerificationUrl) {
281284
this.#log('missing encodedVerificationUrl in server response')
285+
this.#features.lsp.window
286+
.showMessage({
287+
message: 'Subscription request failed. Check the account id.',
288+
type: MessageType.Error,
289+
})
290+
.catch(e => {
291+
this.#log(`showMessage failed: ${(e as Error).message}`)
292+
})
282293
return {
283294
success: false,
284295
failureReason: 'missing encodedVerificationUrl in server response',
285296
}
286297
}
287298

288-
this.#log(`encodedVerificationUrl ${r.encodedVerificationUrl}`)
299+
const uri = r.encodedVerificationUrl
289300

290301
try {
291-
URI.parse(r.encodedVerificationUrl)
302+
URI.parse(uri)
292303
} catch (e) {
293-
this.#log(`invalid encodedVerificationUrl: '${r.encodedVerificationUrl}': ${(e as Error).message}`)
304+
this.#log(`invalid encodedVerificationUrl: '${uri}': ${(e as Error).message}`)
294305
return {
295306
success: false,
296307
failureReason: 'invalid encodedVerificationUrl',
297308
}
298309
}
299310

311+
this.#features.lsp.window
312+
.showMessage({
313+
message: 'Upgraded to [Amazon Q Pro](https://aws.amazon.com/q/)',
314+
type: MessageType.Info,
315+
})
316+
.catch(e => {
317+
this.#log(`showMessage failed: ${(e as Error).message}`)
318+
})
319+
300320
this.#features.lsp.window.showDocument({
301321
external: true, // Client is expected to open the URL in a web browser.
302-
uri: r.encodedVerificationUrl,
322+
uri: uri,
303323
})
304324
} catch (e) {
305325
return {
@@ -308,7 +328,7 @@ export class AgenticChatController implements ChatHandlers {
308328
}
309329
}
310330

311-
this.setUpgradeQMode(params.tabId, 'paidtier')
331+
this.setPaidTierMode(params.tabId, 'paidtier-success')
312332
return { success: true }
313333
} else {
314334
return {
@@ -1971,7 +1991,7 @@ export class AgenticChatController implements ChatHandlers {
19711991

19721992
// TODO handle free tier limit exceeded
19731993
if (isFreeTierLimitError(err)) {
1974-
this.setUpgradeQMode(tabId, 'freetier-limit')
1994+
this.setPaidTierMode(tabId, 'freetier-limit')
19751995
// throw new AmazonQFreeTierLimitError()
19761996
}
19771997

@@ -2239,12 +2259,6 @@ export class AgenticChatController implements ChatHandlers {
22392259
} catch (error) {
22402260
this.#log('Error initializing context commands: ' + error)
22412261
}
2242-
2243-
try {
2244-
this.setUpgradeQMode()
2245-
} catch (err) {
2246-
this.#log('Error initializing Free Tier state: ' + (err as Error).message)
2247-
}
22482262
}
22492263

22502264
onSendFeedback({ tabId, feedbackPayload }: FeedbackParams) {
@@ -2271,7 +2285,7 @@ export class AgenticChatController implements ChatHandlers {
22712285

22722286
this.#chatSessionManagementService.createSession(params.tabId)
22732287

2274-
this.setUpgradeQMode(params.tabId)
2288+
this.setPaidTierMode(params.tabId)
22752289
}
22762290

22772291
onTabChange(params: TabChangeParams) {
@@ -2434,57 +2448,44 @@ export class AgenticChatController implements ChatHandlers {
24342448
* Updates the "Upgrade Q" (subscription tier) state of the UI in the chat component. If `mode` is not given, the user's subscription status is checked by calling the Q service.
24352449
*
24362450
* `mode` behavior:
2437-
* - 'freetier': always show "Upgrade Q" button.
2451+
* - 'freetier': treated as 'freetier-limit' if `this.#paidTierMode='freetier-limit'`.
24382452
* - 'freetier-limit': also show "Free Tier limit reached" card in chat.
24392453
* - This mode is "sticky" until 'paidtier' is passed to override it.
2440-
* - 'paidtier': don't show "Upgrade Q" button.
2454+
* - 'paidtier': disable any "free-tier limit" UI.
24412455
*/
2442-
async setUpgradeQMode(
2443-
tabId?: string,
2444-
mode?: 'paidtier' | 'freetier' | 'freetier-limit' /*, session: ChatSessionService*/
2445-
) {
2446-
this.#log(
2447-
`xxx setUpgradeQMode: mode=${mode} getCodewhispererService=${!!this.#serviceManager?.getCodewhispererService()}`
2448-
)
2456+
setPaidTierMode(tabId?: string, mode?: PaidTierMode) {
2457+
this.#log(`xxx setPaidTierMode: mode=${mode}`)
2458+
24492459
if (mode === 'freetier-limit') {
2450-
this.#freeTierLimit = true // Sticky until 'paidtier' is sent.
2460+
this.#paidTierMode = mode // Sticky until 'paidtier' is sent.
24512461
} else if (mode === 'paidtier') {
2452-
this.#freeTierLimit = false
2453-
} else if (this.#freeTierLimit && (!mode || mode === 'freetier')) {
2462+
this.#paidTierMode = mode
2463+
} else if (this.#paidTierMode === 'freetier-limit' && mode === 'freetier') {
24542464
mode = 'freetier-limit'
24552465
} else if (!mode) {
2456-
try {
2457-
// Note: intentionally async.
2458-
AmazonQTokenServiceManager.getInstance()
2459-
?.getCodewhispererService()
2460-
.getSubscriptionStatus()
2461-
.then(o => {
2462-
this.#log(`xxx getSubscriptionStatus: ${o.status} ${o.encodedVerificationUrl}`)
2463-
this.setUpgradeQMode(tabId, o.status === 'ACTIVE' ? 'paidtier' : 'freetier')
2464-
})
2465-
.catch(err => {
2466-
this.#log(`xxx getSubscriptionStatus failed: ${JSON.stringify(err)}`)
2467-
})
2468-
2469-
// const isFreeTierUser = getSsoConnectionType(this.#features.credentialsProvider) === 'builderId'
2470-
// mode = isFreeTierUser ? 'freetier' : 'paidtier'
2471-
} catch {
2472-
this.#log(`xxx yucky`)
2473-
}
2466+
// Note: intentionally async.
2467+
AmazonQTokenServiceManager.getInstance()
2468+
?.getCodewhispererService()
2469+
.getSubscriptionStatus()
2470+
.then(o => {
2471+
this.#log(`xxx getSubscriptionStatus: ${o.status} ${o.encodedVerificationUrl}`)
2472+
this.setPaidTierMode(tabId, o.status === 'ACTIVE' ? 'paidtier' : 'freetier')
2473+
})
2474+
.catch(err => {
2475+
this.#log(`xxx getSubscriptionStatus failed: ${JSON.stringify(err)}`)
2476+
})
2477+
// const isFreeTierUser = getSsoConnectionType(this.#features.credentialsProvider) === 'builderId'
2478+
// mode = isFreeTierUser ? 'freetier' : 'paidtier'
24742479

24752480
return
24762481
}
24772482

24782483
const o: ChatUpdateParams = {
2479-
tabId: tabId ?? 'xxx',
2480-
state: { inProgress: false },
2481-
data: {
2482-
// Special flag recognized by `chat-client/src/client/mynahUi.ts`.
2483-
placeholderText: 'upgrade-q',
2484-
messages: [],
2485-
},
2484+
tabId: tabId ?? '',
2485+
// data: { messages: [] },
24862486
}
2487-
;(o as any).upgradeQMode = mode
2487+
// Special flag recognized by `chat-client/src/client/mynahUi.ts`.
2488+
;(o as any).paidTierMode = mode
24882489
this.#features.chat.sendChatUpdate(o)
24892490
}
24902491

@@ -2735,6 +2736,9 @@ export class AgenticChatController implements ChatHandlers {
27352736
const updatedOptOutPreference = newConfig.optOutTelemetryPreference
27362737
this.#telemetryService.updateOptOutPreference(updatedOptOutPreference)
27372738
this.#log(`Chat configuration telemetry preference to ${updatedOptOutPreference}`)
2739+
2740+
// Force a service request to get current Q user subscription status.
2741+
this.#paidTierMode = undefined
27382742
}
27392743

27402744
#getTools(session: ChatSessionService) {

server/aws-lsp-codewhisperer/src/language-server/agenticChat/qAgenticChatServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export const QAgenticChatServer =
152152
})
153153

154154
// ;(chat as any).onPromptInputButtonClick((params: any) => {
155-
// chatController.setUpgradeQMode(params.tabId, 'paidtier')
155+
// chatController.setPaidTierMode(params.tabId, 'paidtier')
156156
// })
157157

158158
chat.onButtonClick(params => {

server/aws-lsp-codewhisperer/src/language-server/chat/chatSessionService.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ type DeferredHandler = {
2424
export class ChatSessionService {
2525
public shareCodeWhispererContentWithAWS = false
2626
public pairProgrammingMode: boolean = true
27-
/** Decides whether the Chat UI should show "Upgrade Q" button/card. */
28-
public upgradeQMode: 'paidtier' | 'freetier' | 'freetier-limit' = 'paidtier'
2927
public contextListSent: boolean = false
3028
#abortController?: AbortController
3129
#currentPromptId?: string

server/aws-lsp-codewhisperer/src/language-server/configuration/qConfigurationServer.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ export const QConfigurationServerToken =
112112
token: CancellationToken
113113
): Promise<QConfigurationResponse | void> => {
114114
const section = params.section
115-
logging.info('zzzzzzzz1')
116115

117116
let customizations: Customizations | CustomizationWithMetadata[] = []
118117
let developerProfiles: AmazonQDeveloperProfile[] = []
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type PaidTierMode = 'paidtier' | 'paidtier-success' | 'freetier' | 'freetier-limit'

server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/BaseAmazonQServiceManager.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ export abstract class BaseAmazonQServiceManager<
108108
abstract handleOnUpdateConfiguration(params: UpdateConfigurationParams, token: CancellationToken): Promise<void>
109109

110110
public async handleDidChangeConfiguration(): Promise<void> {
111-
this.logging.debug('xxxx 7 handleDidChangeConfiguration')
112111
if (this.isConfigChangeInProgress) {
113112
this.logging.debug(CONFIGURATION_CHANGE_IN_PROGRESS_MSG)
114113
return
@@ -130,7 +129,6 @@ export abstract class BaseAmazonQServiceManager<
130129
}
131130

132131
protected updateCachedServiceConfig(): void {
133-
this.logging.debug('xxxx 8 updateCachedServiceConfig')
134132
if (this.cachedCodewhispererService) {
135133
const customizationArn = this.configurationCache.getProperty('customizationArn')
136134
this.logging.debug(`Using customization=${customizationArn}`)

0 commit comments

Comments
 (0)