{"version":3,"sources":["src/common.speech/ServiceTelemetryListener.Internal.ts"],"names":[],"mappings":"AAKA,OAAO,EAUH,cAAc,EACd,iBAAiB,EACjB,aAAa,EAChB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,gBAAgB,EAAE,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,OAAO;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACvC;AAED,qBAAa,wBAAyB,YAAW,cAAc,CAAC,aAAa,CAAC;IAC1E,OAAO,CAAC,cAAc,CAAkB;IAExC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,0BAA0B,CAAiB;IACnD,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,6BAA6B,CAAiB;IAEtD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,uBAAuB,CAAS;IAExC,OAAO,CAAC,oBAAoB,CAA8B;IAC1D,OAAO,CAAC,mBAAmB,CAAW;IACtC,OAAO,CAAC,uBAAuB,CAAW;gBAEvB,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAUzE,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAM/C,kBAAkB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAMnD,OAAO,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAiG/B,YAAY,IAAI,MAAM;IA6C7B,IAAW,YAAY,IAAI,OAAO,CAOjC;IAEM,OAAO,IAAI,IAAI;IAItB,OAAO,CAAC,kBAAkB;CAkC7B","file":"ServiceTelemetryListener.Internal.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT license.\r\n\r\n/* eslint-disable max-classes-per-file */\r\n\r\nimport {\r\n    AudioSourceErrorEvent,\r\n    AudioStreamNodeAttachedEvent,\r\n    AudioStreamNodeAttachingEvent,\r\n    AudioStreamNodeDetachedEvent,\r\n    AudioStreamNodeErrorEvent,\r\n    ConnectionEstablishedEvent,\r\n    ConnectionEstablishErrorEvent,\r\n    ConnectionMessageReceivedEvent,\r\n    ConnectionStartEvent,\r\n    IEventListener,\r\n    IStringDictionary,\r\n    PlatformEvent,\r\n} from \"../common/Exports.js\";\r\nimport { ConnectingToServiceEvent, RecognitionTriggeredEvent } from \"./RecognitionEvents.js\";\r\n\r\nexport interface ITelemetry {\r\n    Metrics: IMetric[];\r\n    ReceivedMessages: IStringDictionary<string[]>;\r\n}\r\n\r\nexport interface IMetric {\r\n    End?: string;\r\n    Error?: string;\r\n    Id?: string;\r\n    Name?: string;\r\n    Start?: string;\r\n    PhraseLatencyMs?: number[];\r\n    FirstHypothesisLatencyMs?: number[];\r\n}\r\n\r\nexport class ServiceTelemetryListener implements IEventListener<PlatformEvent> {\r\n    private privIsDisposed: boolean = false;\r\n\r\n    private privRequestId: string;\r\n    private privAudioSourceId: string;\r\n    private privAudioNodeId: string;\r\n\r\n    private privListeningTriggerMetric: IMetric = null;\r\n    private privMicMetric: IMetric = null;\r\n    private privConnectionEstablishMetric: IMetric = null;\r\n\r\n    private privMicStartTime: string;\r\n\r\n    private privConnectionId: string;\r\n    private privConnectionStartTime: string;\r\n\r\n    private privReceivedMessages: IStringDictionary<string[]>;\r\n    private privPhraseLatencies: number[];\r\n    private privHypothesisLatencies: number[];\r\n\r\n    public constructor(requestId: string, audioSourceId: string, audioNodeId: string) {\r\n        this.privRequestId = requestId;\r\n        this.privAudioSourceId = audioSourceId;\r\n        this.privAudioNodeId = audioNodeId;\r\n\r\n        this.privReceivedMessages = {};\r\n        this.privPhraseLatencies = [];\r\n        this.privHypothesisLatencies = [];\r\n    }\r\n\r\n    public phraseReceived(audioReceivedTime: number): void {\r\n        if (audioReceivedTime > 0) { // 0 indicates the time is unknown. Drop it.\r\n            this.privPhraseLatencies.push(Date.now() - audioReceivedTime);\r\n        }\r\n    }\r\n\r\n    public hypothesisReceived(audioReceivedTime: number): void {\r\n        if (audioReceivedTime > 0) { // 0 indicates the time is unknown. Drop it.\r\n            this.privHypothesisLatencies.push(Date.now() - audioReceivedTime);\r\n        }\r\n    }\r\n\r\n    public onEvent(e: PlatformEvent): void {\r\n        if (this.privIsDisposed) {\r\n            return;\r\n        }\r\n\r\n        if (e instanceof RecognitionTriggeredEvent && e.requestId === this.privRequestId) {\r\n            this.privListeningTriggerMetric = {\r\n                End: e.eventTime,\r\n                Name: \"ListeningTrigger\",\r\n                Start: e.eventTime,\r\n            };\r\n        }\r\n\r\n        if (e instanceof AudioStreamNodeAttachingEvent && e.audioSourceId === this.privAudioSourceId && e.audioNodeId === this.privAudioNodeId) {\r\n            this.privMicStartTime = e.eventTime;\r\n        }\r\n\r\n        if (e instanceof AudioStreamNodeAttachedEvent && e.audioSourceId === this.privAudioSourceId && e.audioNodeId === this.privAudioNodeId) {\r\n            this.privMicStartTime = e.eventTime;\r\n        }\r\n\r\n        if (e instanceof AudioSourceErrorEvent && e.audioSourceId === this.privAudioSourceId) {\r\n            if (!this.privMicMetric) {\r\n                this.privMicMetric = {\r\n                    End: e.eventTime,\r\n                    Error: e.error,\r\n                    Name: \"Microphone\",\r\n                    Start: this.privMicStartTime,\r\n                };\r\n            }\r\n        }\r\n\r\n        if (e instanceof AudioStreamNodeErrorEvent && e.audioSourceId === this.privAudioSourceId && e.audioNodeId === this.privAudioNodeId) {\r\n            if (!this.privMicMetric) {\r\n                this.privMicMetric = {\r\n                    End: e.eventTime,\r\n                    Error: e.error,\r\n                    Name: \"Microphone\",\r\n                    Start: this.privMicStartTime,\r\n                };\r\n            }\r\n        }\r\n\r\n        if (e instanceof AudioStreamNodeDetachedEvent && e.audioSourceId === this.privAudioSourceId && e.audioNodeId === this.privAudioNodeId) {\r\n            if (!this.privMicMetric) {\r\n                this.privMicMetric = {\r\n                    End: e.eventTime,\r\n                    Name: \"Microphone\",\r\n                    Start: this.privMicStartTime,\r\n                };\r\n            }\r\n        }\r\n\r\n        if (e instanceof ConnectingToServiceEvent && e.requestId === this.privRequestId) {\r\n            this.privConnectionId = e.sessionId;\r\n        }\r\n\r\n        if (e instanceof ConnectionStartEvent && e.connectionId === this.privConnectionId) {\r\n            this.privConnectionStartTime = e.eventTime;\r\n        }\r\n\r\n        if (e instanceof ConnectionEstablishedEvent && e.connectionId === this.privConnectionId) {\r\n            if (!this.privConnectionEstablishMetric) {\r\n                this.privConnectionEstablishMetric = {\r\n                    End: e.eventTime,\r\n                    Id: this.privConnectionId,\r\n                    Name: \"Connection\",\r\n                    Start: this.privConnectionStartTime,\r\n                };\r\n            }\r\n        }\r\n\r\n        if (e instanceof ConnectionEstablishErrorEvent && e.connectionId === this.privConnectionId) {\r\n            if (!this.privConnectionEstablishMetric) {\r\n                this.privConnectionEstablishMetric = {\r\n                    End: e.eventTime,\r\n                    Error: this.getConnectionError(e.statusCode),\r\n                    Id: this.privConnectionId,\r\n                    Name: \"Connection\",\r\n                    Start: this.privConnectionStartTime,\r\n                };\r\n            }\r\n        }\r\n\r\n        if (e instanceof ConnectionMessageReceivedEvent && e.connectionId === this.privConnectionId) {\r\n            if (e.message && e.message.headers && e.message.headers.path) {\r\n                if (!this.privReceivedMessages[e.message.headers.path]) {\r\n                    this.privReceivedMessages[e.message.headers.path] = new Array<string>();\r\n                }\r\n                const maxMessagesToSend = 50;\r\n                if (this.privReceivedMessages[e.message.headers.path].length < maxMessagesToSend) {\r\n                    this.privReceivedMessages[e.message.headers.path].push(e.networkReceivedTime);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public getTelemetry(): string {\r\n        const metrics = new Array<IMetric>();\r\n\r\n        if (this.privListeningTriggerMetric) {\r\n            metrics.push(this.privListeningTriggerMetric);\r\n        }\r\n\r\n        if (this.privMicMetric) {\r\n            metrics.push(this.privMicMetric);\r\n        }\r\n\r\n        if (this.privConnectionEstablishMetric) {\r\n            metrics.push(this.privConnectionEstablishMetric);\r\n        }\r\n\r\n        if (this.privPhraseLatencies.length > 0) {\r\n            metrics.push({\r\n                PhraseLatencyMs: this.privPhraseLatencies,\r\n            });\r\n        }\r\n\r\n        if (this.privHypothesisLatencies.length > 0) {\r\n            metrics.push({\r\n                FirstHypothesisLatencyMs: this.privHypothesisLatencies,\r\n            });\r\n        }\r\n\r\n        const telemetry: ITelemetry = {\r\n            Metrics: metrics,\r\n            ReceivedMessages: this.privReceivedMessages,\r\n        };\r\n\r\n        const json = JSON.stringify(telemetry);\r\n\r\n        // We dont want to send the same telemetry again. So clean those out.\r\n        this.privReceivedMessages = {};\r\n        this.privListeningTriggerMetric = null;\r\n        this.privMicMetric = null;\r\n        this.privConnectionEstablishMetric = null;\r\n        this.privPhraseLatencies = [];\r\n        this.privHypothesisLatencies = [];\r\n        return json;\r\n    }\r\n\r\n    // Determines if there are any telemetry events to send to the service.\r\n    public get hasTelemetry(): boolean {\r\n        return (Object.keys(this.privReceivedMessages).length !== 0 ||\r\n            this.privListeningTriggerMetric !== null ||\r\n            this.privMicMetric !== null ||\r\n            this.privConnectionEstablishMetric !== null ||\r\n            this.privPhraseLatencies.length !== 0 ||\r\n            this.privHypothesisLatencies.length !== 0);\r\n    }\r\n\r\n    public dispose(): void {\r\n        this.privIsDisposed = true;\r\n    }\r\n\r\n    private getConnectionError(statusCode: number): string {\r\n        /*\r\n        -- Websocket status codes --\r\n        NormalClosure = 1000,\r\n        EndpointUnavailable = 1001,\r\n        ProtocolError = 1002,\r\n        InvalidMessageType = 1003,\r\n        Empty = 1005,\r\n        InvalidPayloadData = 1007,\r\n        PolicyViolation = 1008,\r\n        MessageTooBig = 1009,\r\n        MandatoryExtension = 1010,\r\n        InternalServerError = 1011\r\n        */\r\n\r\n        switch (statusCode) {\r\n            case 400:\r\n            case 1002:\r\n            case 1003:\r\n            case 1005:\r\n            case 1007:\r\n            case 1008:\r\n            case 1009: return \"BadRequest\";\r\n            case 401: return \"Unauthorized\";\r\n            case 403: return \"Forbidden\";\r\n            case 503:\r\n            case 1001: return \"ServerUnavailable\";\r\n            case 500:\r\n            case 1011: return \"ServerError\";\r\n            case 408:\r\n            case 504: return \"Timeout\";\r\n            default: return \"statuscode:\" + statusCode.toString();\r\n        }\r\n    }\r\n}\r\n"]}