/** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { Activity, ExceptionHelper } from '@microsoft/agents-activity' import { Errors } from './errorHelper' import { Configurable } from './configurable' import { DialogContext } from './dialogContext' import omit from 'lodash/omit' import { RecognizerResult, getTopScoringIntent } from './recognizerResult' /** * Configuration options for a recognizer instance. * * @remarks * This interface defines the configuration properties that can be used to customize * the behavior and identification of a recognizer. Recognizers use this configuration * to set up their basic properties and operational parameters. * */ export interface RecognizerConfiguration { /** * Optional unique identifier for the recognizer instance. * * @remarks * The id property allows you to distinguish between multiple recognizer instances * and can be useful for logging, debugging, and telemetry purposes. If not provided, * the recognizer will operate without a specific identifier. * */ id?: string; } /** * Base class for implementing custom recognizers to identify intents and entities from user input. * * @remarks * Recognizers process user input, such as text or speech, and return structured data representing * the recognized intents, entities, and other relevant information. This class provides a foundation * for creating custom recognizers by defining common methods and properties. * */ export class Recognizer extends Configurable implements RecognizerConfiguration { /** * Optional. The unique identifier for the recognizer. */ id?: string /** * To recognize intents and entities in a users utterance. * * @param {DialogContext} _dialogContext Dialog Context. * @param {Partial} _activity Activity. * @param {Record} _telemetryProperties Additional properties to be logged to telemetry with event. * @param {Record} _telemetryMetrics Additional metrics to be logged to telemetry with event. */ recognize ( _dialogContext: DialogContext, _activity: Partial, _telemetryProperties?: Record, _telemetryMetrics?: Record ): Promise { throw ExceptionHelper.generateException( Error, Errors.RecognizeFunctionNotImplemented ) } /** * Creates choose intent result in the case that there are conflicting or ambiguous signals from the recognizers. * * @param {Record} recognizerResults A group of recognizer results. * @returns {RecognizerResult} Recognizer result which is ChooseIntent. */ protected createChooseIntentResult (recognizerResults: Record): RecognizerResult { let text: string = '' let sentiment: Record = {} type candidateType = { id: string; intent: string; score: number; result: RecognizerResult } const candidates = Object.entries(recognizerResults).reduce((candidates: candidateType[], [key, result]) => { text = result.text sentiment = result.sentiment const { intent, score } = getTopScoringIntent(result) if (intent !== 'None') { candidates.push({ id: key, intent, score, result, }) } return candidates }, []) if (candidates.length) { const recognizerResult: RecognizerResult = { text, intents: { ChooseIntent: { score: 1.0 } }, candidates, entities: {}, } return recognizerResult } // just return a `None` intent. const recognizerResult: RecognizerResult = { text, intents: { None: { score: 1.0 } }, entities: {}, sentiment, } return recognizerResult } /** * Uses the RecognizerResult to create a list of properties to be included when tracking the result in telemetry. * * @param {RecognizerResult} recognizerResult Recognizer Result. * @param {Record} telemetryProperties A list of properties to append or override the properties created using the RecognizerResult. * @param {DialogContext} _dialogContext Dialog Context. * @returns {Record} A collection of properties that can be included when calling the TrackEvent method on the TelemetryClient. */ protected fillRecognizerResultTelemetryProperties ( recognizerResult: RecognizerResult, telemetryProperties: Record, _dialogContext?: DialogContext ): Record { const { intent, score } = getTopScoringIntent(recognizerResult) const intents = Object.entries(recognizerResult.intents) const properties: Record = { Text: recognizerResult.text, AlteredText: recognizerResult.alteredText ?? '', TopIntent: intents.length > 0 ? intent : '', TopIntentScore: intents.length > 0 ? score.toString() : '', Intents: intents.length > 0 ? JSON.stringify(recognizerResult.intents) : '', Entities: recognizerResult.entities ? JSON.stringify(recognizerResult.entities) : '', AdditionalProperties: JSON.stringify( omit(recognizerResult, ['text', 'alteredText', 'intents', 'entities']) ), } if (telemetryProperties) { return Object.assign({}, properties, telemetryProperties) } return properties } protected stringifyAdditionalPropertiesOfRecognizerResult (recognizerResult: RecognizerResult): string { const generalProperties = new Set(['text', 'alteredText', 'intents', 'entities']) const additionalProperties: { [key: string]: string } = {} for (const key in recognizerResult) { if (!generalProperties.has(key)) { additionalProperties[key] = recognizerResult[key] } } return Object.keys(additionalProperties).length > 0 ? JSON.stringify(additionalProperties) : '' } }