Skip to content

Commit 56e2475

Browse files
laileni-awsctlai95
andcommitted
feat: Auto fetch models from listAvailableModels API (#2171)
* Updating the model of listAvailableModels (#2064) * fix(amazonq): fix flickering issue for model selection dropdown and agenticCoding toggle (#2065) * fix(amazonq): fix flickering issue for modelId and agenticCoding * fix(amazonq): Fixing flaky tests * feat(amazonq): Fetching models from backend and adding cache implementation. (#2075) * fix: removing and refactoring legacy code before implementing model selection * feat(amazonq): adding cache implementation and fetching models from listAvailableModels api * feat(amazonq): adding selected model in error case * feat(amazonq): adding test cases * fix: addressing comments * fix: fixing test cases and adding modelName to models * fix: minor edits * fix: minor edits * fix: minor modifications in logs * fix: adding default model if api throws any errors * fix: refactoring code * fix: Improve model selection fallback logic when user's preferred model is unavailable (#2089) * fix: if user preferred model does not exist, fall back to default model * fix: minor test changes * fix: to support backward compatibility for vs and eclipse, adding back modelSelection in chat-client (#2095) * fix: check available models from backend before selecting default model from fallback models (#2102) * feat(amazonq): use model display names (#2123) * fix: cached model list should be invalidated on sign out (#2131) * fix: cached model list should be invalidated on sign out * fix test * avoid throwing error * fix: adding default modelId and setting cache ttl to 30 minutes (#2161) * fix: adding defaultmodelId and setting cache ttl to 30 minutes * fix: fixing tests * fix: updating comments * Updating the model of listAvailableModels (#2064) * fix(amazonq): fix flickering issue for model selection dropdown and agenticCoding toggle (#2065) * fix(amazonq): fix flickering issue for modelId and agenticCoding * fix(amazonq): Fixing flaky tests * feat(amazonq): Fetching models from backend and adding cache implementation. (#2075) * fix: removing and refactoring legacy code before implementing model selection * feat(amazonq): adding cache implementation and fetching models from listAvailableModels api * feat(amazonq): adding selected model in error case * feat(amazonq): adding test cases * fix: addressing comments * fix: fixing test cases and adding modelName to models * fix: minor edits * fix: minor edits * fix: minor modifications in logs * fix: adding default model if api throws any errors * fix: refactoring code * fix: Improve model selection fallback logic when user's preferred model is unavailable (#2089) * fix: if user preferred model does not exist, fall back to default model * fix: minor test changes * fix: to support backward compatibility for vs and eclipse, adding back modelSelection in chat-client (#2095) * fix: check available models from backend before selecting default model from fallback models (#2102) * feat(amazonq): use model display names (#2123) * fix: cached model list should be invalidated on sign out (#2131) * fix: cached model list should be invalidated on sign out * fix test * avoid throwing error * fix: adding default modelId and setting cache ttl to 30 minutes (#2161) * fix: adding defaultmodelId and setting cache ttl to 30 minutes * fix: fixing tests * fix: updating comments * fix: lint issue while resolving merge conflicts --------- Co-authored-by: Tai Lai <[email protected]>
1 parent 999f815 commit 56e2475

File tree

21 files changed

+555
-325
lines changed

21 files changed

+555
-325
lines changed

chat-client/src/client/chat.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ import { InboundChatApi, createMynahUi } from './mynahUi'
116116
import { TabFactory } from './tabs/tabFactory'
117117
import { ChatClientAdapter } from '../contracts/chatClientAdapter'
118118
import { toMynahContextCommand, toMynahIcon } from './utils'
119-
import { modelSelectionForRegion } from './texts/modelSelection'
120119

121120
const getDefaultTabConfig = (agenticMode?: boolean) => {
122121
return {
@@ -264,20 +263,6 @@ export const createChat = (
264263
return option
265264
}),
266265
})
267-
} else if (message.params.region) {
268-
// TODO: This can be removed after all clients support aws/chat/listAvailableModels
269-
// get all tabs and update region
270-
const allExistingTabs: MynahUITabStoreModel = mynahUi.getAllTabs()
271-
for (const tabId in allExistingTabs) {
272-
const options = mynahUi.getTabData(tabId).getStore()?.promptInputOptions
273-
mynahUi.updateStore(tabId, {
274-
promptInputOptions: options?.map(option =>
275-
option.id === 'model-selection'
276-
? modelSelectionForRegion[message.params.region]
277-
: option
278-
),
279-
})
280-
}
281266
} else {
282267
tabFactory.setInfoMessages((message.params as ChatOptionsUpdateParams).chatNotifications)
283268
}

chat-client/src/client/mynahUi.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ describe('MynahUI', () => {
260260
sinon.assert.calledThrice(updateStoreSpy)
261261
})
262262

263-
it('should create a new tab if current tab is loading', () => {
263+
it('should create a new tab if current tab is loading', function (done) {
264+
this.timeout(8000)
264265
// clear create tab stub since set up process calls it twice
265266
createTabStub.resetHistory()
266267
getAllTabsStub.returns({ 'tab-1': { store: { loadingChat: true } } })
@@ -274,6 +275,7 @@ describe('MynahUI', () => {
274275

275276
sinon.assert.calledOnceWithExactly(createTabStub, false)
276277
sinon.assert.calledThrice(updateStoreSpy)
278+
done()
277279
})
278280

279281
it('should not create a new tab if one exists already', () => {

chat-client/src/client/mynahUi.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,20 @@ export const handlePromptInputChange = (mynahUi: MynahUI, tabId: string, options
151151
}
152152
}
153153

154+
const updatedPromptInputOptions = promptInputOptions?.map(option => {
155+
option.value = optionsValues[option.id]
156+
return option
157+
})
158+
154159
mynahUi.updateStore(tabId, {
155-
promptInputOptions: promptInputOptions?.map(option => {
156-
option.value = optionsValues[option.id]
157-
return option
158-
}),
160+
promptInputOptions: updatedPromptInputOptions,
161+
})
162+
163+
// Store the updated values in tab defaults for new tabs
164+
mynahUi.updateTabDefaults({
165+
store: {
166+
promptInputOptions: updatedPromptInputOptions,
167+
},
159168
})
160169
}
161170

@@ -414,6 +423,12 @@ export const createMynahUi = (
414423
}
415424

416425
const tabStore = mynahUi.getTabData(tabId).getStore()
426+
const storedPromptInputOptions = mynahUi.getTabDefaults().store?.promptInputOptions
427+
428+
// Retrieve stored model selection and pair programming mode from defaults
429+
if (storedPromptInputOptions) {
430+
defaultTabConfig.promptInputOptions = storedPromptInputOptions
431+
}
417432

418433
// Tabs can be opened through different methods, including server-initiated 'openTab' requests.
419434
// The 'openTab' request is specifically used for loading historical chat sessions with pre-existing messages.

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ChatHistory } from '../features/history'
22
import { TabFactory } from './tabFactory'
33
import * as assert from 'assert'
44
import { pairProgrammingPromptInput } from '../texts/pairProgramming'
5-
import { modelSelectionForRegion } from '../texts/modelSelection'
5+
import { modelSelection } from '../texts/modelSelection'
66

77
describe('tabFactory', () => {
88
describe('getDefaultTabData', () => {
@@ -92,10 +92,7 @@ describe('tabFactory', () => {
9292

9393
const result = tabFactory.createTab(false)
9494

95-
assert.deepStrictEqual(result.promptInputOptions, [
96-
pairProgrammingPromptInput,
97-
modelSelectionForRegion['us-east-1'],
98-
])
95+
assert.deepStrictEqual(result.promptInputOptions, [pairProgrammingPromptInput, modelSelection])
9996
})
10097

10198
it('should not include model selection when only agentic mode is enabled', () => {

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { disclaimerCard } from '../texts/disclaimer'
1111
import { ChatMessage } from '@aws/language-server-runtimes-types'
1212
import { ChatHistory } from '../features/history'
1313
import { pairProgrammingPromptInput, programmerModeCard } from '../texts/pairProgramming'
14-
import { modelSelectionForRegion } from '../texts/modelSelection'
14+
import { modelSelection } from '../texts/modelSelection'
1515

1616
export type DefaultTabData = MynahUIDataModel
1717

@@ -52,10 +52,7 @@ export class TabFactory {
5252
...this.getDefaultTabData(),
5353
...(disclaimerCardActive ? { promptInputStickyCard: disclaimerCard } : {}),
5454
promptInputOptions: this.agenticMode
55-
? [
56-
pairProgrammingPromptInput,
57-
...(this.modelSelectionEnabled ? [modelSelectionForRegion['us-east-1']] : []),
58-
]
55+
? [pairProgrammingPromptInput, ...(this.modelSelectionEnabled ? [modelSelection] : [])]
5956
: [],
6057
cancelButtonWhenLoading: this.agenticMode, // supported for agentic chat only
6158
}

chat-client/src/client/texts/modelSelection.test.ts

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,11 @@
11
import * as assert from 'assert'
2-
import {
3-
BedrockModel,
4-
modelSelectionForRegion,
5-
getModelSelectionChatItem,
6-
modelUnavailableBanner,
7-
modelThrottledBanner,
8-
} from './modelSelection'
2+
import { getModelSelectionChatItem, modelUnavailableBanner, modelThrottledBanner } from './modelSelection'
93
import { ChatItemType } from '@aws/mynah-ui'
104

115
/**
126
* Tests for modelSelection functionality
13-
*
14-
* Note: Some tests are for deprecated code (marked with 'legacy') that is maintained
15-
* for backward compatibility with older clients. These should be removed once
16-
* all clients have been updated to use the new API (aws/chat/listAvailableModels).
177
*/
188
describe('modelSelection', () => {
19-
describe('BedrockModel enum (legacy)', () => {
20-
it('should have the correct model IDs', () => {
21-
assert.strictEqual(BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0, 'CLAUDE_3_7_SONNET_20250219_V1_0')
22-
assert.strictEqual(BedrockModel.CLAUDE_SONNET_4_20250514_V1_0, 'CLAUDE_SONNET_4_20250514_V1_0')
23-
})
24-
})
25-
26-
describe('modelSelectionForRegion (legacy)', () => {
27-
it('should provide all models for us-east-1 region', () => {
28-
const usEast1ModelSelection = modelSelectionForRegion['us-east-1']
29-
assert.ok(usEast1ModelSelection, 'usEast1ModelSelection should exist')
30-
assert.ok(usEast1ModelSelection.type === 'select', 'usEast1ModelSelection should be type select')
31-
assert.ok(Array.isArray(usEast1ModelSelection.options), 'options should be an array')
32-
assert.strictEqual(usEast1ModelSelection.options.length, 2, 'should have 2 options')
33-
34-
const modelIds = usEast1ModelSelection.options.map(option => option.value)
35-
assert.ok(modelIds.includes(BedrockModel.CLAUDE_SONNET_4_20250514_V1_0), 'should include Claude Sonnet 4')
36-
assert.ok(
37-
modelIds.includes(BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0),
38-
'should include Claude Sonnet 3.7'
39-
)
40-
})
41-
42-
it('should provide all models for eu-central-1 region', () => {
43-
const euCentral1ModelSelection = modelSelectionForRegion['eu-central-1']
44-
assert.ok(euCentral1ModelSelection, 'euCentral1ModelSelection should exist')
45-
assert.ok(euCentral1ModelSelection.type === 'select', 'euCentral1ModelSelection should be type select')
46-
assert.ok(Array.isArray(euCentral1ModelSelection.options), 'options should be an array')
47-
assert.strictEqual(euCentral1ModelSelection.options.length, 2, 'should have 2 option')
48-
49-
const modelIds = euCentral1ModelSelection.options.map(option => option.value)
50-
assert.ok(modelIds.includes(BedrockModel.CLAUDE_SONNET_4_20250514_V1_0), 'should include Claude Sonnet 4')
51-
assert.ok(
52-
modelIds.includes(BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0),
53-
'should include Claude Sonnet 3.7'
54-
)
55-
})
56-
})
57-
589
describe('getModelSelectionChatItem', () => {
5910
it('should return a chat item with the correct model name', () => {
6011
const modelName = 'Claude Sonnet 4'

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type ModelDetails = {
1313
}
1414

1515
const modelRecord: Record<BedrockModel, ModelDetails> = {
16-
[BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0]: { label: 'Claude Sonnet 3.7' },
16+
[BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0]: { label: 'Claude 3.7 Sonnet' },
1717
[BedrockModel.CLAUDE_SONNET_4_20250514_V1_0]: { label: 'Claude Sonnet 4' },
1818
}
1919

@@ -22,24 +22,16 @@ const modelOptions = Object.entries(modelRecord).map(([value, { label }]) => ({
2222
label,
2323
}))
2424

25-
const modelSelection: ChatItemFormItem = {
25+
export const modelSelection: ChatItemFormItem = {
2626
type: 'select',
2727
id: 'model-selection',
28-
options: modelOptions,
2928
mandatory: true,
3029
hideMandatoryIcon: true,
30+
options: modelOptions,
3131
border: false,
3232
autoWidth: true,
3333
}
3434

35-
/**
36-
* @deprecated use aws/chat/listAvailableModels server request instead
37-
*/
38-
export const modelSelectionForRegion: Record<string, ChatItemFormItem> = {
39-
'us-east-1': modelSelection,
40-
'eu-central-1': modelSelection,
41-
}
42-
4335
export const getModelSelectionChatItem = (modelName: string): ChatItem => ({
4436
type: ChatItemType.DIRECTIVE,
4537
contentHorizontalAlignment: 'center',

server/aws-lsp-codewhisperer/src/client/sigv4/codewhisperersigv4client.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* THIS FILE IS AUTOGENERATED BY 'generateServiceClient.ts'.
44
* DO NOT EDIT BY HAND.
55
*/
6-
6+
77
import {Request} from 'aws-sdk/lib/request';
88
import {Response} from 'aws-sdk/lib/response';
99
import {AWSError} from 'aws-sdk/lib/error';

server/aws-lsp-codewhisperer/src/client/token/bearer-token-service.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3622,6 +3622,10 @@
36223622
"shape": "Models",
36233623
"documentation": "<p>List of available models</p>"
36243624
},
3625+
"defaultModel": {
3626+
"shape": "Model",
3627+
"documentation": "<p>Default model set by the client</p>"
3628+
},
36253629
"nextToken": {
36263630
"shape": "Base64EncodedPaginationToken",
36273631
"documentation": "<p>Token for retrieving the next page of results</p>"
@@ -3955,6 +3959,10 @@
39553959
"shape": "ModelId",
39563960
"documentation": "<p>Unique identifier for the model</p>"
39573961
},
3962+
"modelName": {
3963+
"shape": "ModelName",
3964+
"documentation": "<p>User-facing display name</p>"
3965+
},
39583966
"description": {
39593967
"shape": "Description",
39603968
"documentation": "<p>Description of the model</p>"
@@ -3972,6 +3980,13 @@
39723980
"min": 1,
39733981
"pattern": "[a-zA-Z0-9_:.-]+"
39743982
},
3983+
"ModelName": {
3984+
"type": "string",
3985+
"documentation": "<p>Identifier for the model Name</p>",
3986+
"max": 1024,
3987+
"min": 1,
3988+
"pattern": "[a-zA-Z0-9-_.]+"
3989+
},
39753990
"ModelMetadata": {
39763991
"type": "structure",
39773992
"members": {

server/aws-lsp-codewhisperer/src/client/token/codewhispererbearertokenclient.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* THIS FILE IS AUTOGENERATED BY 'generateServiceClient.ts'.
44
* DO NOT EDIT BY HAND.
55
*/
6+
67
import {Request} from 'aws-sdk/lib/request';
78
import {Response} from 'aws-sdk/lib/response';
89
import {AWSError} from 'aws-sdk/lib/error';
@@ -1132,6 +1133,10 @@ declare namespace CodeWhispererBearerTokenClient {
11321133
* List of available models
11331134
*/
11341135
models: Models;
1136+
/**
1137+
* Default model set by the client
1138+
*/
1139+
defaultModel?: Model;
11351140
/**
11361141
* Token for retrieving the next page of results
11371142
*/
@@ -1240,6 +1245,10 @@ declare namespace CodeWhispererBearerTokenClient {
12401245
* Unique identifier for the model
12411246
*/
12421247
modelId: ModelId;
1248+
/**
1249+
* User-facing display name
1250+
*/
1251+
modelName?: ModelName;
12431252
/**
12441253
* Description of the model
12451254
*/
@@ -1250,6 +1259,7 @@ declare namespace CodeWhispererBearerTokenClient {
12501259
modelMetadata?: ModelMetadata;
12511260
}
12521261
export type ModelId = string;
1262+
export type ModelName = string;
12531263
export interface ModelMetadata {
12541264
/**
12551265
* Maximum number of input tokens the model can process

0 commit comments

Comments
 (0)