@hahnpro/ms-speech-sdk
Version:
Microsoft Cognitive Services Speech SDK for JavaScript
1 lines • 34.3 kB
Source Map (JSON)
{"version":3,"sources":["src/common.speech/DialogServiceAdapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAUH,YAAY,EAKf,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAEH,qBAAqB,EACrB,kBAAkB,EAElB,sBAAsB,EAQtB,uBAAuB,EAE1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAKH,qBAAqB,EAKxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAC9F,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAEhF,qBAAa,oBAAqB,SAAQ,qBAAqB;IAC3D,OAAO,CAAC,0BAA0B,CAAyB;IAE3D,OAAO,CAAC,qBAAqB,CAAe;IAE5C,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,oBAAoB,CAAU;IACtC,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,UAAU,CAA2B;IAK7C,OAAO,CAAC,oBAAoB,CAAgC;gBAGxD,cAAc,EAAE,eAAe,EAC/B,iBAAiB,EAAE,kBAAkB,EACrC,WAAW,EAAE,YAAY,EACzB,gBAAgB,EAAE,gBAAgB,EAClC,sBAAsB,EAAE,sBAAsB;IAyBrC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAwBxC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/C,SAAS,CAAC,2BAA2B,CAAC,iBAAiB,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;cAmInF,iBAAiB,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,kBAAkB,EACtC,SAAS,EAAE,qBAAqB,EAChC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cA8CjB,UAAU,CACtB,QAAQ,EAAE,eAAe,EACzB,eAAe,EAAE,CAAC,CAAC,EAAE,uBAAuB,KAAK,IAAI,EACrD,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC;IAgDhB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,4BAA4B;YA6ItB,gBAAgB;YAchB,gBAAgB;YAWhB,oBAAoB;IAQlC,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,qBAAqB;IAqD7B,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,qBAAqB;CAoChC","file":"DialogServiceAdapter.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license.\n\nimport {\n ReplayableAudioNode\n} from \"../common.browser/Exports.js\";\nimport { SendingAgentContextMessageEvent } from \"../common/DialogEvents.js\";\nimport {\n BackgroundEvent,\n ConnectionEvent,\n ConnectionMessage,\n createGuid,\n createNoDashGuid,\n Deferred,\n DialogEvent,\n Events,\n EventSource,\n IAudioSource,\n IAudioStreamNode,\n IConnection,\n MessageType,\n ServiceEvent,\n} from \"../common/Exports.js\";\nimport { AudioOutputFormatImpl } from \"../sdk/Audio/AudioOutputFormat.js\";\nimport { PullAudioOutputStreamImpl } from \"../sdk/Audio/AudioOutputStream.js\";\nimport { AudioStreamFormatImpl } from \"../sdk/Audio/AudioStreamFormat.js\";\nimport {\n ActivityReceivedEventArgs,\n CancellationErrorCode,\n CancellationReason,\n DialogServiceConfig,\n DialogServiceConnector,\n PropertyCollection,\n PropertyId,\n RecognitionEventArgs,\n ResultReason,\n SessionEventArgs,\n SpeechRecognitionCanceledEventArgs,\n SpeechRecognitionEventArgs,\n SpeechRecognitionResult,\n TurnStatusReceivedEventArgs,\n} from \"../sdk/Exports.js\";\nimport { DialogServiceTurnStateManager } from \"./DialogServiceTurnStateManager.js\";\nimport {\n CancellationErrorCodePropertyName,\n EnumTranslation,\n ISpeechConfigAudioDevice,\n RecognitionStatus,\n ServiceRecognizerBase,\n SimpleSpeechPhrase,\n SpeechDetected,\n SpeechHypothesis,\n SpeechKeyword,\n} from \"./Exports.js\";\nimport { IAuthentication } from \"./IAuthentication.js\";\nimport { IConnectionFactory } from \"./IConnectionFactory.js\";\nimport { RecognizerConfig } from \"./RecognizerConfig.js\";\nimport { ActivityPayloadResponse } from \"./ServiceMessages/ActivityResponsePayload.js\";\nimport { InvocationSource } from \"./ServiceMessages/InvocationSource.js\";\nimport { ClientDetectedKeyword, KeywordDetectionType, OnRejectAction } from \"./ServiceMessages/KeywordDetection/KeywordDetection.js\";\nimport { RecognitionMode } from \"./ServiceMessages/PhraseDetection/PhraseDetectionContext.js\";\nimport { SpeechConnectionMessage } from \"./SpeechConnectionMessage.Internal.js\";\n\nexport class DialogServiceAdapter extends ServiceRecognizerBase {\n private privDialogServiceConnector: DialogServiceConnector;\n\n private privDialogAudioSource: IAudioSource;\n\n private privConnectionLoop: Promise<void>;\n private terminateMessageLoop: boolean;\n private agentConfigSent: boolean;\n private privLastResult: SpeechRecognitionResult;\n private privEvents: EventSource<DialogEvent>;\n\n // Turns are of two kinds:\n // 1: SR turns, end when the SR result is returned and then turn end.\n // 2: Service turns where an activity is sent by the service along with the audio.\n private privTurnStateManager: DialogServiceTurnStateManager;\n\n public constructor(\n authentication: IAuthentication,\n connectionFactory: IConnectionFactory,\n audioSource: IAudioSource,\n recognizerConfig: RecognizerConfig,\n dialogServiceConnector: DialogServiceConnector) {\n\n super(authentication, connectionFactory, audioSource, recognizerConfig, dialogServiceConnector);\n\n this.privEvents = new EventSource<DialogEvent>();\n this.privDialogServiceConnector = dialogServiceConnector;\n this.receiveMessageOverride = (): Promise<void> => this.receiveDialogMessageOverride();\n this.privTurnStateManager = new DialogServiceTurnStateManager();\n this.recognizeOverride =\n (recoMode: RecognitionMode, successCallback: (e: SpeechRecognitionResult) => void, errorCallback: (e: string) => void): Promise<void> =>\n this.listenOnce(recoMode, successCallback, errorCallback);\n this.postConnectImplOverride = (connection: Promise<IConnection>): Promise<IConnection> => this.dialogConnectImpl(connection);\n this.configConnectionOverride = (connection: IConnection): Promise<IConnection> => this.configConnection(connection);\n this.disconnectOverride = (): Promise<void> => this.privDisconnect();\n this.privDialogAudioSource = audioSource;\n\n this.agentConfigSent = false;\n this.privLastResult = null;\n this.connectionEvents.attach((connectionEvent: ConnectionEvent): void => {\n if (connectionEvent.name === \"ConnectionClosedEvent\") {\n this.terminateMessageLoop = true;\n }\n });\n }\n\n public async sendMessage(message: string): Promise<void> {\n const interactionGuid: string = createGuid();\n const requestId: string = createNoDashGuid();\n\n const agentMessage: any = {\n context: {\n interactionId: interactionGuid\n },\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n messagePayload: JSON.parse(message),\n version: 0.5\n };\n\n const agentMessageJson = JSON.stringify(agentMessage);\n const connection: IConnection = await this.fetchConnection();\n await connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n \"agent\",\n requestId,\n \"application/json\",\n agentMessageJson));\n\n }\n\n protected async privDisconnect(): Promise<void> {\n await this.cancelRecognition(this.privRequestSession.sessionId,\n this.privRequestSession.requestId,\n CancellationReason.Error,\n CancellationErrorCode.NoError,\n \"Disconnecting\");\n\n this.terminateMessageLoop = true;\n this.agentConfigSent = false;\n return;\n }\n\n protected processTypeSpecificMessages(connectionMessage: SpeechConnectionMessage): Promise<boolean> {\n\n const resultProps: PropertyCollection = new PropertyCollection();\n if (connectionMessage.messageType === MessageType.Text) {\n resultProps.setProperty(PropertyId.SpeechServiceResponse_JsonResult, connectionMessage.textBody);\n }\n\n let result: SpeechRecognitionResult;\n let processed: boolean;\n\n switch (connectionMessage.path.toLowerCase()) {\n case \"speech.phrase\":\n const speechPhrase: SimpleSpeechPhrase = SimpleSpeechPhrase.fromJSON(connectionMessage.textBody, this.privRequestSession.currentTurnAudioOffset);\n\n this.privRequestSession.onPhraseRecognized(speechPhrase.Offset + speechPhrase.Duration);\n\n if (speechPhrase.RecognitionStatus !== RecognitionStatus.TooManyRequests && speechPhrase.RecognitionStatus !== RecognitionStatus.Error) {\n const args: SpeechRecognitionEventArgs = this.fireEventForResult(speechPhrase, resultProps);\n this.privLastResult = args.result;\n\n if (!!this.privDialogServiceConnector.recognized) {\n try {\n this.privDialogServiceConnector.recognized(this.privDialogServiceConnector, args);\n /* eslint-disable no-empty */\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n }\n processed = true;\n break;\n case \"speech.hypothesis\":\n const hypothesis: SpeechHypothesis = SpeechHypothesis.fromJSON(connectionMessage.textBody, this.privRequestSession.currentTurnAudioOffset);\n\n result = new SpeechRecognitionResult(\n this.privRequestSession.requestId,\n ResultReason.RecognizingSpeech,\n hypothesis.Text,\n hypothesis.Duration,\n hypothesis.Offset,\n hypothesis.Language,\n hypothesis.LanguageDetectionConfidence,\n undefined,\n undefined,\n hypothesis.asJson(),\n resultProps);\n\n this.privRequestSession.onHypothesis(hypothesis.Offset);\n\n const ev = new SpeechRecognitionEventArgs(result, hypothesis.Offset, this.privRequestSession.sessionId);\n\n if (!!this.privDialogServiceConnector.recognizing) {\n try {\n this.privDialogServiceConnector.recognizing(this.privDialogServiceConnector, ev);\n /* eslint-disable no-empty */\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n processed = true;\n break;\n case \"speech.keyword\":\n const keyword: SpeechKeyword = SpeechKeyword.fromJSON(connectionMessage.textBody, this.privRequestSession.currentTurnAudioOffset);\n\n result = new SpeechRecognitionResult(\n this.privRequestSession.requestId,\n keyword.Status === \"Accepted\" ? ResultReason.RecognizedKeyword : ResultReason.NoMatch,\n keyword.Text,\n keyword.Duration,\n keyword.Offset,\n undefined,\n undefined,\n undefined,\n undefined,\n keyword.asJson(),\n resultProps);\n\n if (keyword.Status !== \"Accepted\") {\n this.privLastResult = result;\n }\n\n const event = new SpeechRecognitionEventArgs(result, result.duration, result.resultId);\n\n if (!!this.privDialogServiceConnector.recognized) {\n try {\n this.privDialogServiceConnector.recognized(this.privDialogServiceConnector, event);\n /* eslint-disable no-empty */\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n processed = true;\n break;\n case \"audio\":\n {\n const audioRequestId = connectionMessage.requestId.toUpperCase();\n const turn = this.privTurnStateManager.GetTurn(audioRequestId);\n try {\n // Empty binary message signals end of stream.\n if (!connectionMessage.binaryBody) {\n turn.endAudioStream();\n } else {\n turn.audioStream.write(connectionMessage.binaryBody);\n }\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n processed = true;\n break;\n\n case \"response\":\n {\n this.handleResponseMessage(connectionMessage);\n }\n processed = true;\n break;\n\n default:\n break;\n }\n const defferal = new Deferred<boolean>();\n defferal.resolve(processed);\n return defferal.promise;\n }\n\n // Cancels recognition.\n protected async cancelRecognition(\n sessionId: string,\n requestId: string,\n cancellationReason: CancellationReason,\n errorCode: CancellationErrorCode,\n error: string): Promise<void> {\n\n this.terminateMessageLoop = true;\n\n if (!!this.privRequestSession.isRecognizing) {\n await this.privRequestSession.onStopRecognizing();\n }\n\n if (!!this.privDialogServiceConnector.canceled) {\n const properties: PropertyCollection = new PropertyCollection();\n properties.setProperty(CancellationErrorCodePropertyName, CancellationErrorCode[errorCode]);\n\n const cancelEvent: SpeechRecognitionCanceledEventArgs = new SpeechRecognitionCanceledEventArgs(\n cancellationReason,\n error,\n errorCode,\n undefined,\n sessionId);\n\n try {\n this.privDialogServiceConnector.canceled(this.privDialogServiceConnector, cancelEvent);\n /* eslint-disable no-empty */\n } catch { }\n\n if (!!this.privSuccessCallback) {\n const result: SpeechRecognitionResult = new SpeechRecognitionResult(\n undefined, // ResultId\n ResultReason.Canceled,\n undefined, // Text\n undefined, // Duration\n undefined, // Offset\n undefined, // Language\n undefined, // Language Detection Confidence\n undefined, // Speaker Id\n error,\n undefined, // Json\n properties);\n try {\n this.privSuccessCallback(result);\n this.privSuccessCallback = undefined;\n /* eslint-disable no-empty */\n } catch { }\n }\n }\n }\n\n protected async listenOnce(\n recoMode: RecognitionMode,\n successCallback: (e: SpeechRecognitionResult) => void,\n errorCallback: (e: string) => void\n ): Promise<void> {\n this.privRecognizerConfig.recognitionMode = recoMode;\n\n this.privSuccessCallback = successCallback;\n this.privErrorCallback = errorCallback;\n\n this.privRequestSession.startNewRecognition();\n this.privRequestSession.listenForServiceTelemetry(this.privDialogAudioSource.events);\n\n this.privRecognizerConfig.parameters.setProperty(PropertyId.Speech_SessionId, this.privRequestSession.sessionId);\n\n // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\n const conPromise: Promise<IConnection> = this.connectImpl();\n\n const preAudioPromise: Promise<void> = this.sendPreAudioMessages();\n\n const node: IAudioStreamNode = await this.privDialogAudioSource.attach(this.privRequestSession.audioNodeId);\n const format: AudioStreamFormatImpl = await this.privDialogAudioSource.format;\n const deviceInfo: ISpeechConfigAudioDevice = await this.privDialogAudioSource.deviceInfo;\n\n const audioNode = new ReplayableAudioNode(node, format.avgBytesPerSec);\n await this.privRequestSession.onAudioSourceAttachCompleted(audioNode, false);\n\n this.privRecognizerConfig.SpeechServiceConfig.Context.audio = { source: deviceInfo };\n\n try {\n await conPromise;\n await preAudioPromise;\n } catch (error) {\n await this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.ConnectionFailure, error as string);\n return Promise.resolve();\n }\n\n const sessionStartEventArgs: SessionEventArgs = new SessionEventArgs(this.privRequestSession.sessionId);\n\n if (!!this.privRecognizer.sessionStarted) {\n this.privRecognizer.sessionStarted(this.privRecognizer, sessionStartEventArgs);\n }\n\n const audioSendPromise = this.sendAudio(audioNode);\n\n // /* eslint-disable no-empty */\n audioSendPromise.then((): void => { /* add? return true;*/ }, async (error: string): Promise<void> => {\n await this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.RuntimeError, error);\n });\n }\n\n // Establishes a websocket connection to the end point.\n private dialogConnectImpl(connection: Promise<IConnection>): Promise<IConnection> {\n this.privConnectionLoop = this.startMessageLoop();\n return connection;\n }\n\n private receiveDialogMessageOverride(): Promise<void> {\n\n // we won't rely on the cascading promises of the connection since we want to continually be available to receive messages\n const communicationCustodian: Deferred<void> = new Deferred<void>();\n\n const loop = async (): Promise<void> => {\n try {\n const isDisposed: boolean = this.isDisposed();\n const terminateMessageLoop = (!this.isDisposed() && this.terminateMessageLoop);\n if (isDisposed || terminateMessageLoop) {\n // We're done.\n communicationCustodian.resolve(undefined);\n return;\n }\n\n const connection: IConnection = await this.fetchConnection();\n const message: ConnectionMessage = await connection.read();\n\n if (!message) {\n return loop();\n }\n\n const connectionMessage = SpeechConnectionMessage.fromConnectionMessage(message);\n\n switch (connectionMessage.path.toLowerCase()) {\n case \"turn.start\":\n {\n const turnRequestId = connectionMessage.requestId.toUpperCase();\n const audioSessionReqId = this.privRequestSession.requestId.toUpperCase();\n\n // turn started by the service\n if (turnRequestId !== audioSessionReqId) {\n this.privTurnStateManager.StartTurn(turnRequestId);\n } else {\n this.privRequestSession.onServiceTurnStartResponse();\n }\n }\n break;\n\n case \"speech.startdetected\":\n const speechStartDetected: SpeechDetected = SpeechDetected.fromJSON(connectionMessage.textBody, this.privRequestSession.currentTurnAudioOffset);\n\n const speechStartEventArgs = new RecognitionEventArgs(speechStartDetected.Offset, this.privRequestSession.sessionId);\n\n if (!!this.privRecognizer.speechStartDetected) {\n this.privRecognizer.speechStartDetected(this.privRecognizer, speechStartEventArgs);\n }\n\n break;\n\n case \"speech.enddetected\":\n\n let json: string;\n\n if (connectionMessage.textBody.length > 0) {\n json = connectionMessage.textBody;\n } else {\n // If the request was empty, the JSON returned is empty.\n json = \"{ Offset: 0 }\";\n }\n\n const speechStopDetected: SpeechDetected = SpeechDetected.fromJSON(json, this.privRequestSession.currentTurnAudioOffset);\n\n this.privRequestSession.onServiceRecognized(speechStopDetected.Offset);\n\n const speechStopEventArgs = new RecognitionEventArgs(speechStopDetected.Offset, this.privRequestSession.sessionId);\n\n if (!!this.privRecognizer.speechEndDetected) {\n this.privRecognizer.speechEndDetected(this.privRecognizer, speechStopEventArgs);\n }\n break;\n\n case \"turn.end\":\n {\n const turnEndRequestId = connectionMessage.requestId.toUpperCase();\n\n const audioSessionReqId = this.privRequestSession.requestId.toUpperCase();\n\n // turn started by the service\n if (turnEndRequestId !== audioSessionReqId) {\n this.privTurnStateManager.CompleteTurn(turnEndRequestId);\n } else {\n // Audio session turn\n\n const sessionStopEventArgs: SessionEventArgs = new SessionEventArgs(this.privRequestSession.sessionId);\n await this.privRequestSession.onServiceTurnEndResponse(false);\n\n if (!this.privRecognizerConfig.isContinuousRecognition || this.privRequestSession.isSpeechEnded || !this.privRequestSession.isRecognizing) {\n if (!!this.privRecognizer.sessionStopped) {\n this.privRecognizer.sessionStopped(this.privRecognizer, sessionStopEventArgs);\n }\n }\n\n // report result to promise.\n if (!!this.privSuccessCallback && this.privLastResult) {\n try {\n this.privSuccessCallback(this.privLastResult);\n this.privLastResult = null;\n } catch (e) {\n if (!!this.privErrorCallback) {\n this.privErrorCallback(e as string);\n }\n }\n // Only invoke the call back once.\n // and if it's successful don't invoke the\n // error after that.\n this.privSuccessCallback = undefined;\n this.privErrorCallback = undefined;\n }\n }\n }\n break;\n\n default:\n try {\n const processed = await this.processTypeSpecificMessages(connectionMessage);\n if (!processed) {\n if (!!this.serviceEvents) {\n this.serviceEvents.onEvent(new ServiceEvent(connectionMessage.path.toLowerCase(), connectionMessage.textBody));\n }\n }\n } catch (e) {\n //\n }\n }\n const ret: Promise<void> = loop();\n\n return ret;\n } catch (error) {\n this.terminateMessageLoop = true;\n communicationCustodian.resolve();\n }\n };\n\n loop().catch((reason: string): void => {\n Events.instance.onEvent(new BackgroundEvent(reason));\n });\n\n return communicationCustodian.promise;\n }\n\n private async startMessageLoop(): Promise<void> {\n\n this.terminateMessageLoop = false;\n\n try {\n await this.receiveDialogMessageOverride();\n } catch (error) {\n await this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.RuntimeError, error as string);\n }\n\n return Promise.resolve();\n }\n\n // Takes an established websocket connection to the endpoint and sends speech configuration information.\n private async configConnection(connection: IConnection): Promise<IConnection> {\n if (this.terminateMessageLoop) {\n this.terminateMessageLoop = false;\n return Promise.reject(\"Connection to service terminated.\");\n }\n\n await this.sendSpeechServiceConfig(connection, this.privRequestSession, this.privRecognizerConfig.SpeechServiceConfig.serialize());\n await this.sendAgentConfig(connection);\n return connection;\n }\n\n private async sendPreAudioMessages(): Promise<void> {\n const connection: IConnection = await this.fetchConnection();\n this.addKeywordContextData();\n await this.sendSpeechContext(connection, true);\n await this.sendAgentContext(connection);\n await this.sendWaveHeader(connection);\n }\n\n private sendAgentConfig(connection: IConnection): Promise<void> {\n if (this.agentConfig && !this.agentConfigSent) {\n\n if (this.privRecognizerConfig\n .parameters\n .getProperty(PropertyId.Conversation_DialogType) === DialogServiceConfig.DialogTypes.CustomCommands) {\n const config = this.agentConfig.get();\n config.botInfo.commandsCulture = this.privRecognizerConfig.parameters.getProperty(PropertyId.SpeechServiceConnection_RecoLanguage, \"en-us\");\n this.agentConfig.set(config);\n }\n this.onEvent(new SendingAgentContextMessageEvent(this.agentConfig));\n\n const agentConfigJson = this.agentConfig.toJsonString();\n\n // guard against sending this multiple times on one connection\n this.agentConfigSent = true;\n\n return connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n \"agent.config\",\n this.privRequestSession.requestId,\n \"application/json\",\n agentConfigJson));\n }\n\n return;\n }\n\n private sendAgentContext(connection: IConnection): Promise<void> {\n const guid: string = createGuid();\n\n const speechActivityTemplate = this.privDialogServiceConnector.properties.getProperty(PropertyId.Conversation_Speech_Activity_Template);\n\n const agentContext: any = {\n channelData: \"\",\n context: {\n interactionId: guid\n },\n messagePayload: typeof speechActivityTemplate === undefined ? undefined : speechActivityTemplate,\n version: 0.5\n };\n\n const agentContextJson = JSON.stringify(agentContext);\n\n return connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n \"speech.agent.context\",\n this.privRequestSession.requestId,\n \"application/json\",\n agentContextJson));\n }\n\n private fireEventForResult(serviceResult: SimpleSpeechPhrase, properties: PropertyCollection): SpeechRecognitionEventArgs {\n const resultReason: ResultReason = EnumTranslation.implTranslateRecognitionResult(serviceResult.RecognitionStatus);\n\n const result = new SpeechRecognitionResult(\n this.privRequestSession.requestId,\n resultReason,\n serviceResult.DisplayText,\n serviceResult.Duration,\n serviceResult.Offset,\n serviceResult.Language,\n serviceResult.LanguageDetectionConfidence,\n undefined,\n undefined,\n serviceResult.asJson(),\n properties);\n\n const ev = new SpeechRecognitionEventArgs(result, serviceResult.Offset, this.privRequestSession.sessionId);\n return ev;\n }\n\n private handleResponseMessage(responseMessage: SpeechConnectionMessage): void {\n // \"response\" messages can contain either \"message\" (activity) or \"MessageStatus\" data. Fire the appropriate\n // event according to the message type that's specified.\n const responsePayload: { messageType: string } = JSON.parse(responseMessage.textBody) as { messageType: string };\n switch (responsePayload.messageType.toLowerCase()) {\n case \"message\":\n const responseRequestId = responseMessage.requestId.toUpperCase();\n const activityPayload: ActivityPayloadResponse = ActivityPayloadResponse.fromJSON(responseMessage.textBody);\n const turn = this.privTurnStateManager.GetTurn(responseRequestId);\n\n // update the conversation Id\n if (activityPayload.conversationId) {\n const updateAgentConfig = this.agentConfig.get();\n updateAgentConfig.botInfo.conversationId = activityPayload.conversationId;\n this.agentConfig.set(updateAgentConfig);\n }\n\n const pullAudioOutputStream: PullAudioOutputStreamImpl = turn.processActivityPayload(\n activityPayload,\n AudioOutputFormatImpl.fromSpeechSynthesisOutputFormatString(this.privDialogServiceConnector.properties.getProperty(PropertyId.SpeechServiceConnection_SynthOutputFormat, undefined)));\n const activity = new ActivityReceivedEventArgs(activityPayload.messagePayload, pullAudioOutputStream);\n if (!!this.privDialogServiceConnector.activityReceived) {\n try {\n this.privDialogServiceConnector.activityReceived(this.privDialogServiceConnector, activity);\n /* eslint-disable-next-line no-empty */\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n break;\n\n case \"messagestatus\":\n if (!!this.privDialogServiceConnector.turnStatusReceived) {\n try {\n this.privDialogServiceConnector.turnStatusReceived(\n this.privDialogServiceConnector,\n new TurnStatusReceivedEventArgs(responseMessage.textBody));\n /* eslint-disable-next-line no-empty */\n } catch (error) {\n // Not going to let errors in the event handler\n // trip things up.\n }\n }\n break;\n\n default:\n Events.instance.onEvent(\n new BackgroundEvent(`Unexpected response of type ${responsePayload.messageType}. Ignoring.`));\n break;\n }\n }\n\n private onEvent(event: DialogEvent): void {\n this.privEvents.onEvent(event);\n Events.instance.onEvent(event);\n }\n\n private addKeywordContextData(): void {\n const keywordPropertyValue: string = this.privRecognizerConfig.parameters.getProperty(\"SPEECH-KeywordsToDetect\");\n if (keywordPropertyValue === undefined) {\n return;\n }\n\n const keywordOffsetPropertyValue: string = this.privRecognizerConfig.parameters\n .getProperty(\"SPEECH-KeywordsToDetect-Offsets\");\n const keywordDurationPropertyValue: string = this.privRecognizerConfig.parameters\n .getProperty(\"SPEECH-KeywordsToDetect-Durations\");\n\n const keywords = keywordPropertyValue.split(\";\");\n const keywordOffsets = keywordOffsetPropertyValue === undefined ? [] : keywordOffsetPropertyValue.split(\";\");\n const keywordDurations = keywordDurationPropertyValue === undefined ? [] : keywordDurationPropertyValue.split(\";\");\n\n const keywordDefinitionArray: ClientDetectedKeyword[] = [];\n for (let i = 0; i < keywords.length; i++) {\n const definition: ClientDetectedKeyword = {\n text: keywords[i]\n };\n if (i < keywordOffsets.length) {\n definition.startOffset = Number(keywordOffsets[i]);\n }\n if (i < keywordDurations.length) {\n definition.duration = Number(keywordDurations[i]);\n }\n keywordDefinitionArray.push(definition);\n }\n\n this.speechContext.getContext().invocationSource = InvocationSource.VoiceActivationWithKeyword;\n this.speechContext.getContext().keywordDetection = [{\n clientDetectedKeywords: keywordDefinitionArray,\n onReject: { action: OnRejectAction.EndOfTurn },\n type: KeywordDetectionType.StartTrigger\n }];\n }\n}\n"]}