Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
* Will be deleted or merged.
*/

import * as path from 'path'

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "path"

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "path"
import {
ChatTriggerType,
CodeWhispererStreamingServiceException,
GenerateAssistantResponseCommandInput,
GenerateAssistantResponseCommandOutput,
SendMessageCommandInput,
Expand Down Expand Up @@ -68,7 +69,7 @@
import { ChatTelemetryController } from '../chat/telemetry/chatTelemetryController'
import { QuickAction } from '../chat/quickActions'
import { Metric } from '../../shared/telemetry/metric'
import { getErrorMessage, isAwsError, isNullish, isObject } from '../../shared/utils'
import { getErrorMessage, getHttpStatusCode, isNullish } from '../../shared/utils'
import { HELP_MESSAGE } from '../chat/constants'
import { TelemetryService } from '../../shared/telemetry/telemetryService'
import {
Expand Down Expand Up @@ -1078,11 +1079,22 @@
tabId: string,
metric: Metric<CombinedConversationEvent>
): ChatResult | ResponseError<ChatResult> {
if (isAwsError(err) || (isObject(err) && 'statusCode' in err && typeof err.statusCode === 'number')) {
metric.setDimension('cwsprChatRepsonseCode', err.statusCode ?? 400)
this.#telemetryController.emitMessageResponseError(tabId, metric.metric, err.requestId, err.message)
let errorMessage: string
let requestID: string | undefined

if (err instanceof CodeWhispererStreamingServiceException) {
errorMessage = err.message
requestID = err.$metadata.requestId
} else {
errorMessage = 'Not a CodeWhispererStreamingServiceException.'
if (err instanceof Error || err?.message) {
errorMessage += ` Error is: ${err.message}`
}
}

metric.setDimension('cwsprChatRepsonseCode', getHttpStatusCode(err) ?? 0)
this.#telemetryController.emitMessageResponseError(tabId, metric.metric, requestID, errorMessage)

// return non-model errors back to the client as errors
if (!(err instanceof ModelServiceException)) {
this.#log(`unknown error ${err instanceof Error ? JSON.stringify(err) : 'unknown'}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,25 +250,25 @@ export class ChatTelemetryController {
requestId?: string,
errorReason?: string
) {
this.emitConversationMetric(
{
name: ChatTelemetryEventName.MessageResponseError,
data: {
cwsprChatHasCodeSnippet: metric.cwsprChatHasCodeSnippet,
cwsprChatTriggerInteraction: metric.cwsprChatTriggerInteraction,
cwsprChatUserIntent: metric.cwsprChatUserIntent,
cwsprChatProgrammingLanguage: metric.cwsprChatProgrammingLanguage,
cwsprChatActiveEditorTotalCharacters: metric.cwsprChatActiveEditorTotalCharacters,
cwsprChatActiveEditorImportCount: metric.cwsprChatActiveEditorImportCount,
cwsprChatRepsonseCode: metric.cwsprChatRepsonseCode,
cwsprChatRequestLength: metric.cwsprChatRequestLength,
cwsprChatConversationType: metric.cwsprChatConversationType,
requestId: requestId,
reasonDesc: getTelemetryReasonDesc(errorReason),
},
this.#telemetry.emitMetric({
name: ChatTelemetryEventName.MessageResponseError,
data: {
cwsprChatHasCodeSnippet: metric.cwsprChatHasCodeSnippet,
cwsprChatTriggerInteraction: metric.cwsprChatTriggerInteraction,
cwsprChatUserIntent: metric.cwsprChatUserIntent,
cwsprChatProgrammingLanguage: metric.cwsprChatProgrammingLanguage,
cwsprChatActiveEditorTotalCharacters: metric.cwsprChatActiveEditorTotalCharacters,
cwsprChatActiveEditorImportCount: metric.cwsprChatActiveEditorImportCount,
cwsprChatRepsonseCode: metric.cwsprChatRepsonseCode,
cwsprChatRequestLength: metric.cwsprChatRequestLength,
cwsprChatConversationType: metric.cwsprChatConversationType,
requestId: requestId,
reasonDesc: getTelemetryReasonDesc(errorReason),
credentialStartUrl: this.#credentialsProvider.getConnectionMetadata()?.sso?.startUrl,
result: 'Succeeded',
[CONVERSATION_ID_METRIC_KEY]: this.getConversationId(tabId),
},
tabId
)
})
}

public enqueueCodeDiffEntry(params: Omit<InsertToCursorPositionParams, 'name'>) {
Expand Down
23 changes: 23 additions & 0 deletions server/aws-lsp-codewhisperer/src/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { distance } from 'fastest-levenshtein'
import { Suggestion } from './codeWhispererService'
import { CodewhispererCompletionType } from './telemetry/types'
import { BUILDER_ID_START_URL, crashMonitoringDirName, driveLetterRegex, MISSING_BEARER_TOKEN_ERROR } from './constants'
import { CodeWhispererStreamingServiceException } from '@amzn/codewhisperer-streaming'
import { ServiceException } from '@smithy/smithy-client'
export type SsoConnectionType = 'builderId' | 'identityCenter' | 'none'

export function isAwsError(error: unknown): error is AWSError {
Expand Down Expand Up @@ -301,3 +303,24 @@ export function safeGet<T, E extends Error>(object: T | undefined, customError?:
export function isStringOrNull(object: any): object is string | null {
return typeof object === 'string' || object === null
}

// Port of implementation in AWS Toolkit for VSCode
// https://github.com/aws/aws-toolkit-vscode/blob/c22efa03e73b241564c8051c35761eb8620edb83/packages/core/src/shared/errors.ts#L648
export function getHttpStatusCode(err: unknown): number | undefined {
if (hasResponse(err) && err?.$response?.statusCode !== undefined) {
return err?.$response?.statusCode
}
if (hasMetadata(err) && err.$metadata?.httpStatusCode !== undefined) {
return err.$metadata?.httpStatusCode
}

return undefined
}

function hasResponse<T>(error: T): error is T & Pick<ServiceException, '$response'> {
return typeof (error as { $response?: unknown })?.$response === 'object'
}

function hasMetadata<T>(error: T): error is T & Pick<CodeWhispererStreamingServiceException, '$metadata'> {
return typeof (error as { $metadata?: unknown })?.$metadata === 'object'
}
Loading