Skip to content

Commit f8a6f83

Browse files
ahusseinalihayemaxi
authored andcommitted
Add logic that uses IAM Q Dev Client
1 parent 7c2d5cf commit f8a6f83

File tree

12 files changed

+10640
-9711
lines changed

12 files changed

+10640
-9711
lines changed

package-lock.json

Lines changed: 10544 additions & 9687 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/amazonq/src/api.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import { SendMessageCommandOutput, SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client'
67
import { GenerateAssistantResponseCommandOutput, GenerateAssistantResponseRequest } from '@amzn/codewhisperer-streaming'
78
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
89
import { ChatSession } from 'aws-core-vscode/codewhispererChat'
@@ -12,7 +13,11 @@ export default {
1213
chatApi: {
1314
async chat(request: GenerateAssistantResponseRequest): Promise<GenerateAssistantResponseCommandOutput> {
1415
const chatSession = new ChatSession()
15-
return chatSession.chat(request)
16+
return chatSession.chatSso(request)
17+
},
18+
async chatIam(request: SendMessageRequest): Promise<SendMessageCommandOutput> {
19+
const chatSession = new ChatSession()
20+
return chatSession.chatIam(request)
1621
},
1722
},
1823
authApi: {

packages/core/src/amazonq/extApi.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import vscode from 'vscode'
77
import { VSCODE_EXTENSION_ID } from '../shared/utilities'
8+
import { SendMessageCommandOutput, SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client'
89
import { GenerateAssistantResponseCommandOutput, GenerateAssistantResponseRequest } from '@amzn/codewhisperer-streaming'
910
import { FeatureAuthState } from '../codewhisperer/util/authUtil'
1011
import { ToolkitError } from '../shared'
@@ -16,6 +17,7 @@ import { ToolkitError } from '../shared'
1617
export interface api {
1718
chatApi: {
1819
chat(request: GenerateAssistantResponseRequest): Promise<GenerateAssistantResponseCommandOutput>
20+
chatIam(request: SendMessageRequest): Promise<SendMessageCommandOutput>
1921
}
2022
authApi: {
2123
reauthIfNeeded(): Promise<void>

packages/core/src/codewhisperer/util/authUtil.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as localizedText from '../../shared/localizedText'
88
import { Auth } from '../../auth/auth'
99
import { ToolkitError, isNetworkError, tryRun } from '../../shared/errors'
1010
import { getSecondaryAuth, setScopes } from '../../auth/secondaryAuth'
11-
import { isCloud9, isSageMaker } from '../../shared/extensionUtilities'
11+
import { isAmazonQ, isCloud9, isSageMaker } from '../../shared/extensionUtilities'
1212
import { AmazonQPromptSettings } from '../../shared/settings'
1313
import {
1414
scopesCodeWhispererCore,
@@ -60,7 +60,7 @@ export const isValidCodeWhispererCoreConnection = (conn?: Connection): conn is C
6060
}
6161

6262
if (isSageMaker()) {
63-
return isIamConnection(conn)
63+
return isIamConnection(conn) || (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes))
6464
}
6565

6666
return (
@@ -70,6 +70,10 @@ export const isValidCodeWhispererCoreConnection = (conn?: Connection): conn is C
7070
}
7171
/** Superset that includes all of CodeWhisperer + Amazon Q */
7272
export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => {
73+
if (isSageMaker() && isAmazonQ()) {
74+
return isIamConnection(conn) || (isSsoConnection(conn) && hasScopes(conn, amazonQScopes))
75+
}
76+
7377
return (
7478
(isSsoConnection(conn) || isBuilderIdConnection(conn)) &&
7579
isValidCodeWhispererCoreConnection(conn) &&
@@ -442,9 +446,6 @@ export class AuthUtil {
442446
if (conn === undefined) {
443447
return buildFeatureAuthState(AuthStates.disconnected)
444448
}
445-
if (!isSsoConnection(conn)) {
446-
throw new ToolkitError(`Connection "${conn.id}" is not a valid type: ${conn.type}`)
447-
}
448449

449450
// default to expired to indicate reauth is needed if unmodified
450451
const state: FeatureAuthState = buildFeatureAuthState(AuthStates.expired)
@@ -453,7 +454,7 @@ export class AuthUtil {
453454
return state
454455
}
455456

456-
if (isBuilderIdConnection(conn) || isIdcSsoConnection(conn)) {
457+
if (isBuilderIdConnection(conn) || isIdcSsoConnection(conn) || isSageMaker()) {
457458
if (isValidCodeWhispererCoreConnection(conn)) {
458459
state[Features.codewhispererCore] = AuthStates.connected
459460
}

packages/core/src/codewhispererChat/clients/chat/v0/chat.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import { SendMessageCommandOutput, SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client'
67
import { GenerateAssistantResponseCommandOutput, GenerateAssistantResponseRequest } from '@amzn/codewhisperer-streaming'
78
import * as vscode from 'vscode'
89
import { ToolkitError } from '../../../../shared/errors'
910
import { createCodeWhispererChatStreamingClient } from '../../../../shared/clients/codewhispererChatClient'
11+
import { createQDeveloperStreamingClient } from '../../../../shared/clients/qDeveloperChatClient'
1012

1113
export class ChatSession {
1214
private sessionId?: string
@@ -28,8 +30,28 @@ export class ChatSession {
2830
public setSessionID(id?: string) {
2931
this.sessionId = id
3032
}
33+
async chatIam(chatRequest: SendMessageRequest): Promise<SendMessageCommandOutput> {
34+
const client = await createQDeveloperStreamingClient()
3135

32-
async chat(chatRequest: GenerateAssistantResponseRequest): Promise<GenerateAssistantResponseCommandOutput> {
36+
const response = await client.sendMessage(chatRequest)
37+
if (!response.sendMessageResponse) {
38+
throw new ToolkitError(
39+
`Empty chat response. Session id: ${this.sessionId} Request ID: ${response.$metadata.requestId}`
40+
)
41+
}
42+
43+
const responseStream = response.sendMessageResponse
44+
for await (const event of responseStream) {
45+
if ('messageMetadataEvent' in event) {
46+
this.sessionId = event.messageMetadataEvent?.conversationId
47+
break
48+
}
49+
}
50+
51+
return response
52+
}
53+
54+
async chatSso(chatRequest: GenerateAssistantResponseRequest): Promise<GenerateAssistantResponseCommandOutput> {
3355
const client = await createCodeWhispererChatStreamingClient()
3456

3557
if (this.sessionId !== undefined && chatRequest.conversationState !== undefined) {

packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
*/
55

66
import {
7+
ConversationState,
78
CursorState,
89
DocumentSymbol,
9-
GenerateAssistantResponseRequest,
1010
RelevantTextDocument,
1111
SymbolType,
1212
TextDocument,
@@ -37,7 +37,7 @@ export const supportedLanguagesList = [
3737
const filePathSizeLimit = 4_000
3838
const customerMessageSizeLimit = 4_000
3939

40-
export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): GenerateAssistantResponseRequest {
40+
export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): { conversationState: ConversationState } {
4141
let document: TextDocument | undefined = undefined
4242
let cursorState: CursorState | undefined = undefined
4343

packages/core/src/codewhispererChat/controllers/chat/controller.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { Event as VSCodeEvent, Uri } from 'vscode'
66
import { EditorContextExtractor } from '../../editor/context/extractor'
77
import { ChatSessionStorage } from '../../storages/chatSession'
8-
import { Messenger, StaticTextResponseType } from './messenger/messenger'
8+
import { Messenger, MessengerResponseType, StaticTextResponseType } from './messenger/messenger'
99
import {
1010
PromptMessage,
1111
ChatTriggerType,
@@ -34,10 +34,8 @@ import { EditorContentController } from '../../../amazonq/commons/controllers/co
3434
import { EditorContextCommand } from '../../commands/registerCommands'
3535
import { PromptsGenerator } from './prompts/promptsGenerator'
3636
import { TriggerEventsStorage } from '../../storages/triggerEvents'
37-
import {
38-
CodeWhispererStreamingServiceException,
39-
GenerateAssistantResponseCommandOutput,
40-
} from '@amzn/codewhisperer-streaming'
37+
import { SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client'
38+
import { CodeWhispererStreamingServiceException, __MetadataBearer } from '@amzn/codewhisperer-streaming'
4139
import { UserIntentRecognizer } from './userIntent/userIntentRecognizer'
4240
import { CWCTelemetryHelper, recordTelemetryChatRunCommand } from './telemetryHelper'
4341
import { CodeWhispererTracker } from '../../../codewhisperer/tracker/codewhispererTracker'
@@ -54,6 +52,7 @@ import { getHttpStatusCode, AwsClientResponseError } from '../../../shared/error
5452
import { uiEventRecorder } from '../../../amazonq/util/eventRecorder'
5553
import { globals } from '../../../shared'
5654
import { telemetry } from '../../../shared/telemetry'
55+
import { isSsoConnection } from '../../../auth/connection'
5756

5857
export interface ChatControllerMessagePublishers {
5958
readonly processPromptChatMessage: MessagePublisher<PromptMessage>
@@ -656,12 +655,24 @@ export class ChatController {
656655
request
657656
)}`
658657
)
659-
let response: GenerateAssistantResponseCommandOutput | undefined = undefined
658+
let response: MessengerResponseType | undefined = undefined
660659
session.createNewTokenSource()
661660
try {
662661
this.messenger.sendInitalStream(tabID, triggerID)
663662
this.telemetryHelper.setConversationStreamStartTime(tabID)
664-
response = await session.chat(request)
663+
if (isSsoConnection(AuthUtil.instance.conn)) {
664+
const { $metadata, generateAssistantResponseResponse } = await session.chatSso(request)
665+
response = {
666+
$metadata: $metadata,
667+
message: generateAssistantResponseResponse,
668+
}
669+
} else {
670+
const { $metadata, sendMessageResponse } = await session.chatIam(request as SendMessageRequest)
671+
response = {
672+
$metadata: $metadata,
673+
message: sendMessageResponse,
674+
}
675+
}
665676
this.telemetryHelper.recordEnterFocusConversation(triggerEvent.tabID)
666677
this.telemetryHelper.recordStartConversation(triggerEvent, triggerPayload)
667678

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import {
1313
QuickActionMessage,
1414
} from '../../../view/connector/connector'
1515
import { EditorContextCommandType } from '../../../commands/registerCommands'
16+
import { ChatResponseStream as qdevChatResponseStream } from '@amzn/amazon-q-developer-streaming-client'
1617
import {
18+
ChatResponseStream as cwChatResponseStream,
1719
CodeWhispererStreamingServiceException,
18-
GenerateAssistantResponseCommandOutput,
1920
SupplementaryWebLink,
21+
__MetadataBearer,
2022
} from '@amzn/codewhisperer-streaming'
2123
import { ChatMessage, ErrorMessage, FollowUp, Suggestion } from '../../../view/connector/connector'
2224
import { ChatSession } from '../../../clients/chat/v0/chat'
@@ -37,6 +39,10 @@ import { extractAuthFollowUp } from '../../../../amazonq/util/authUtils'
3739

3840
export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help'
3941

42+
export type MessengerResponseType = __MetadataBearer & {
43+
message?: AsyncIterable<cwChatResponseStream | qdevChatResponseStream>
44+
}
45+
4046
export class Messenger {
4147
public constructor(
4248
private readonly dispatcher: AppToWebViewMessageDispatcher,
@@ -103,7 +109,7 @@ export class Messenger {
103109
}
104110

105111
public async sendAIResponse(
106-
response: GenerateAssistantResponseCommandOutput,
112+
response: MessengerResponseType,
107113
session: ChatSession,
108114
tabID: string,
109115
triggerID: string,
@@ -116,7 +122,7 @@ export class Messenger {
116122
let relatedSuggestions: Suggestion[] = []
117123
let codeBlockLanguage: string = 'plaintext'
118124

119-
if (response.generateAssistantResponseResponse === undefined) {
125+
if (response.message === undefined) {
120126
throw new ToolkitError(
121127
`Empty response from CodeWhisperer Streaming service. Request ID: ${response.$metadata.requestId}`
122128
)
@@ -133,7 +139,7 @@ export class Messenger {
133139
const eventCounts = new Map<string, number>()
134140
waitUntil(
135141
async () => {
136-
for await (const chatEvent of response.generateAssistantResponseResponse!) {
142+
for await (const chatEvent of response.message!) {
137143
for (const key of keys(chatEvent)) {
138144
if ((chatEvent[key] as any) !== undefined) {
139145
eventCounts.set(key, (eventCounts.get(key) ?? 0) + 1)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { QDeveloperStreaming } from '@amzn/amazon-q-developer-streaming-client'
6+
import { getCodewhispererConfig } from '../../codewhisperer/client/codewhisperer'
7+
import { getUserAgent } from '../telemetry/util'
8+
import { ConfiguredRetryStrategy } from '@smithy/util-retry'
9+
import { AuthUtil } from '../../codewhisperer/util/authUtil'
10+
11+
// Create a client for featureDev streaming based off of aws sdk v3
12+
export async function createQDeveloperStreamingClient(): Promise<QDeveloperStreaming> {
13+
const cwsprConfig = getCodewhispererConfig()
14+
const credentials = await AuthUtil.instance.getCredentials()
15+
const streamingClient = new QDeveloperStreaming({
16+
region: cwsprConfig.region,
17+
endpoint: cwsprConfig.endpoint,
18+
credentials: credentials,
19+
customUserAgent: getUserAgent(),
20+
// SETTING max attempts to 0 FOR BETA. RE-ENABLE FOR RE-INVENT
21+
// Implement exponential back off starting with a base of 500ms (500 + attempt^10)
22+
retryStrategy: new ConfiguredRetryStrategy(0, (attempt: number) => 500 + attempt ** 10),
23+
})
24+
return streamingClient
25+
}

packages/toolkit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@
709709
{
710710
"id": "aws.amazonq.codewhisperer",
711711
"name": "%AWS.amazonq.codewhisperer.title%",
712-
"when": "!isCloud9 && !aws.isSageMaker && !aws.toolkit.amazonq.dismissed && !aws.explorer.showAuthView"
712+
"when": "!isCloud9 && !aws.toolkit.amazonq.dismissed && !aws.explorer.showAuthView"
713713
},
714714
{
715715
"id": "aws.explorer",

0 commit comments

Comments
 (0)