/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */ import * as CLM from '@conversationlearner/models' ; import * as React from 'react' ; import * as OF from 'office-ui-fabric-react' ; import * as Util from '../Utils/util' ; import * as BB from 'botbuilder' ; import TagsReadOnly from '../components/TagsReadOnly' ; import { compareTwoStrings } from 'string-similarity' ; import { deepCopy , getDefaultEntityMap } from './util' ; import { ImportedAction } from '../types/models' ; import { getValueConditionName , findNumberFromMemory , isValueConditionTrue , isEnumConditionTrue } from './actionCondition' ; import { fromLogTag } from '../types' ; const MAX_SAMPLE_INPUT_LENGTH = 150 ; export const CARD_MATCH_THRESHOLD = 0.25 ; export const DialogQueryParams = { id : "id" } ; export interface DialogRenderData { dialogMode : CLM . DialogMode ; memories : CLM . Memory [ ] ; prevMemories : CLM . Memory [ ] ; textVariations : CLM . TextVariation [ ] ; roundIndex : number | null ; scoreResponse ? : CLM . ScoreResponse ; scoreInput ? : CLM . ScoreInput ; selectedActionId ? : string ; extractResponses ? : CLM . ExtractResponse [ ] ; } export function getReplayError ( activity ) { if ( ! activity ?. channelData ?. clData ) { return null ; } const clData = activity . channelData . clData ; return clData . replayError ; } export function internalConflict ( textVariation , trainDialog , roundIndex ) { for ( let i = 0 ; i < trainDialog . rounds . length ; i = i + 1 ) { if ( roundIndex === null || i !== roundIndex ) { for ( const tv of trainDialog . rounds [ i ] . extractorStep . textVariations ) { if ( textVariation . text === tv . text ) { if ( ! CLM . ModelUtils . areEqualTextVariations ( textVariation , tv ) ) { return CLM . ModelUtils . ToExtractResponse ( tv ) ; } } } } } return null ; } param tvA param tvB export function isConflictingTextVariation ( tvA , tvB ) { if ( tvA . text . toLowerCase ( ) !== tvB . text . toLowerCase ( ) ) { return false ; } if ( tvA . labelEntities . length !== tvB . labelEntities . length ) { return true ; } const sameEntities = tvA . labelEntities . every ( le => tvB . labelEntities . find ( ale => ale . entityId === le . entityId && ale . entityText === le . entityText && ale . startCharIndex === le . startCharIndex && ale . endCharIndex === le . endCharIndex ) ) ; if ( sameEntities ) { return false ; } return true ; } param tvA param tvB export function isIncompatibleTextVariation ( tvA , tvB ) { if ( tvA . labelEntities . length !== tvB . labelEntities . length ) { return true ; } const sameEntitiesPresent = tvA . labelEntities . every ( le => tvB . labelEntities . find ( ale => ale . entityId === le . entityId ) ) ; if ( sameEntitiesPresent ) { return false ; } return true ; } param attemptedTextVariation param trainDialogs export function getCorrectedDialogs ( attemptedTextVariation , trainDialogs ) { const correctedDialogs = [ ] ; for ( const td of Util . deepCopy ( trainDialogs ) ) { let isConflict = false ; for ( const r of td . rounds ) { let isConflictWithinRound = false ; for ( const [ i , tv ] of r . extractorStep . textVariations . entries ( ) ) { const isConflictingWithTextVariation = isConflictingTextVariation ( tv , attemptedTextVariation ) ; if ( isConflictingWithTextVariation ) { r . extractorStep . textVariations [ i ] = Util . deepCopy ( attemptedTextVariation ) ; td . validity = CLM . Validity . WARNING ; } isConflictWithinRound = isConflictWithinRound || isConflictingWithTextVariation ; } if ( isConflictWithinRound ) { for ( const tv of r . extractorStep . textVariations ) { const isIncompatibleWithOtherVariations = isIncompatibleTextVariation ( tv , attemptedTextVariation ) ; if ( isIncompatibleWithOtherVariations ) { td . validity = CLM . Validity . INVALID ; } } } isConflict = isConflict || isConflictWithinRound ; } if ( isConflict ) { correctedDialogs . push ( td ) ; } } return correctedDialogs ; } export function cleanText ( rawText ) { if ( ! rawText ) { return "" ; } return rawText . trim ( ) . split ( '&nbsp;' ) . join ( " " ) . split ( " </" ) . join ( "</" ) . split ( "\n" ) . join ( "" ) . split ( "<b>" ) . join ( "**" ) . split ( "</b>" ) . join ( "**" ) . split ( "<i>" ) . join ( "*" ) . split ( "</i>" ) . join ( "*" ) . split ( "<strong>" ) . join ( "**_" ) . split ( "</strong>" ) . join ( "_**" ) . split ( "<br>" ) . join ( "" ) . split ( "<br/>" ) . join ( "" ) . split ( "<br />" ) . join ( "" ) . split ( '&gt;' ) . join ( "" ) . replace ( /[\n\r]+/g , '' ) ; } export function activityIndexFromRound ( trainDialog , roundIndex , scoreIndex ) { if ( ! roundIndex ) { return undefined ; } let activityIndex = 0 ; let currentRoundIndex = 0 ; for ( const round of trainDialog . rounds ) { if ( currentRoundIndex === roundIndex ) { if ( ! scoreIndex ) { return activityIndex ; } else { return activityIndex + scoreIndex ; } } else { currentRoundIndex = currentRoundIndex + 1 ; activityIndex = activityIndex + 1 + round . scorerSteps . length ; } } return activityIndex ; } export function matchedActivityIndex ( selectedActivity , activities ) { if ( ! selectedActivity || activities . length === 0 ) { return null ; } else { const clDataSelected = selectedActivity . channelData . clData ; const index = activities . findIndex ( a => { const clData = a . channelData . clData ; return ( clData . senderType === clDataSelected . senderType && clData . roundIndex === clDataSelected . roundIndex && clData . scoreIndex === clDataSelected . scoreIndex ) ; } ) ; if ( index < 0 ) { console . log ( 'Failed to find selected activity' ) ; return null ; } return index ; } } export function filterDummyEntities ( memories ) { return memories . filter ( m => { return ( m . entityValues . length > 0 ) ; } ) ; } export function hasEndSession ( trainDialog , allActions ) { if ( trainDialog . rounds . length === 0 ) { return false ; } const lastRound = trainDialog . rounds [ trainDialog . rounds . length - 1 ] ; if ( lastRound . scorerSteps . length === 0 ) { return false ; } const lastScorerStep = lastRound . scorerSteps [ lastRound . scorerSteps . length - 1 ] ; const lastAction = allActions . find ( a => a . actionId === lastScorerStep . labelAction ) ; if ( lastAction ) { return lastAction . actionType === CLM . ActionTypes . END_SESSION || lastAction . actionType === CLM . ActionTypes . CHANGE_MODEL ; } return false ; } export function getBestAction ( scoreResponse , allActions , canEndSession ) { const scoredActions = scoreResponse . scoredActions ; let best ; for ( const test of scoredActions ) { const action = allActions . find ( a => a . actionId === test . actionId ) ; if ( action ) { if ( canEndSession || action . actionType !== CLM . ActionTypes . END_SESSION ) { if ( ! best || test . score > best . score ) { best = test ; } } } } return best ; } export function getBestUnscoredAction ( scoreResponse , actions , entities , memories ) { if ( scoreResponse . unscoredActions . length === 0 ) { return undefined ; } for ( const action of scoreResponse . unscoredActions ) { if ( isActionIdAvailable ( action . actionId , actions , entities , memories ) ) { const scoredAction = { ... action , score : 1 } ; delete scoredAction . reason ; return scoredAction ; } } const unavailableScoredAction = { ... scoreResponse . unscoredActions [ 0 ] , score : 1 } ; delete unavailableScoredAction . reason ; return unavailableScoredAction ; } export function entityInMemory ( entityId , entities , memories ) { const entity = entities . find ( e => e . entityId === entityId ) ; if ( ! entity ) { return { match : false , name : 'ERROR' } ; } const memory = memories . find ( m => m . entityName === entity . entityName ) ; return { match : ( memory !== undefined ) , name : entity . entityName } ; } export function isActionIdAvailable ( actionId , actions , entities , memories ) { const action = actions . find ( a => a . actionId === actionId ) ; if ( ! action ) { return false ; } return isActionAvailable ( action , entities , memories ) ; } export function isActionAvailable ( action , entities , memories ) { for ( const entityId of action . requiredEntities ) { const found = entityInMemory ( entityId , entities , memories ) ; if ( ! found . match ) { return false ; } } for ( const entityId of action . negativeEntities ) { const found = entityInMemory ( entityId , entities , memories ) ; if ( found . match ) { return false ; } } if ( action . requiredConditions ) { for ( const condition of action . requiredConditions ) { const result = convertToScorerCondition ( condition , entities , memories ) ; if ( ! result . match ) { return false ; } } } if ( action . negativeConditions ) { for ( const condition of action . negativeConditions ) { const result = convertToScorerCondition ( condition , entities , memories ) ; if ( result . match ) { return false ; } } } return true ; } export function convertToScorerCondition ( condition , entities , memories ) { const entity = entities . find ( e => e . entityId === condition . entityId ) ; if ( ! entity ) { return { match : false , name : 'ERROR' } ; } const memory = memories . find ( m => m . entityName === entity . entityName ) ; if ( condition . valueId ) { const enumValue = entity . enumValues ?. find ( ev => ev . enumValueId === condition . valueId ) ; const value = enumValue ? enumValue . enumValue : "NOT FOUND" ; const match = memory !== undefined && isEnumConditionTrue ( condition , memory ) ; return { match , name : `${ entity . entityName } == ${ value }` } ; } else if ( condition . value ) { const name = getValueConditionName ( entity , condition ) ; let match = false ; if ( memory ) { const numberValue = findNumberFromMemory ( memory , entity . isMultivalue ) ; if ( numberValue ) { match = isValueConditionTrue ( condition , numberValue ) ; } } return { match , name } ; } else { return { match : false , name : `Unknown Condition Type` } ; } } export function dialogSampleInput ( dialog ) { const userInputs = [ ] ; let round = 0 ; let length = 0 ; while ( round < dialog . rounds . length && length < MAX_SAMPLE_INPUT_LENGTH ) { let userInput = ( dialog as CLM . LogDialog ) . rounds [ round ] . extractorStep . text ; if ( ! userInput && ( dialog as CLM . TrainDialog ) . rounds [ round ] . extractorStep . textVariations [ 0 ] ) { userInput = ( dialog as CLM . TrainDialog ) . rounds [ round ] . extractorStep . textVariations [ 0 ] . text ; } if ( ! userInput ) { userInput = "MISSING USER INPUT" ; } userInputs . push ( userInput ) ; length = length + userInput . length ; round = round + 1 ; } return userInputs . join ( " ◾️ " ) . slice ( 0 , MAX_SAMPLE_INPUT_LENGTH ) ; } export function trainDialogFirstInput ( trainDialog ) { if ( trainDialog . rounds ?. length > 0 ) { return trainDialog . rounds [ 0 ] . extractorStep . textVariations [ 0 ] . text ; } return "" ; } export function trainDialogLastInput ( trainDialog ) { if ( trainDialog . rounds ?. length > 0 ) { return trainDialog . rounds [ trainDialog . rounds . length - 1 ] . extractorStep . textVariations [ 0 ] . text ; } } export function trainDialogLastResponse ( trainDialog , actions , entities ) { if ( trainDialog . rounds ?. length > 0 ) { const scorerSteps = trainDialog . rounds [ trainDialog . rounds . length - 1 ] . scorerSteps ; if ( scorerSteps . length > 0 ) { const actionId = scorerSteps [ scorerSteps . length - 1 ] . labelAction ; const action = actions . find ( a => a . actionId === actionId ) ; if ( action ) { return CLM . ActionBase . GetPayload ( action , getDefaultEntityMap ( entities ) ) ; } } } } export function trainDialogRenderTags ( trainDialog ) { return ( < span className = { `${ OF . FontClassNames . mediumPlus }` } data - testid = "train-dialogs-tags" > { trainDialog . tags . length === 0 ? < OF . Icon iconName = "Remove" className = "cl-icon" / > : < TagsReadOnly tags = { trainDialog . tags } / > } < /span>); } export function trainDialogRenderDescription ( trainDialog ) { return trainDialog . description ? < i > { trainDialog . description } < /i> : dialogSampleInput(trainDialog); } export function hasImportActions ( trainDialog ) { for ( const round of trainDialog . rounds ) { for ( const scorerStep of round . scorerSteps ) { if ( scorerStep . labelAction === CLM . CL_STUB_IMPORT_ACTION_ID ) { return true ; } } } return false ; } export function getMostSevereReplayError ( activities ) { let worstReplayError = null ; for ( const a of activities ) { const clData = a . channelData . clData ; if ( clData ?. replayError ) { if ( clData . replayError . errorLevel === CLM . ReplayErrorLevel . BLOCKING ) { return clData . replayError ; } else if ( clData . replayError . errorLevel === CLM . ReplayErrorLevel . ERROR ) { worstReplayError = clData . replayError ; } else if ( clData . replayError . errorLevel === CLM . ReplayErrorLevel . WARNING && ( ! worstReplayError || worstReplayError . errorLevel !== CLM . ReplayErrorLevel . ERROR ) ) { worstReplayError = clData . replayError ; } } } return worstReplayError ; } export function getTrainDialogValidity ( trainDialog , activities ) { const worstReplayError = getMostSevereReplayError ( activities ) ; if ( worstReplayError ) { if ( worstReplayError . errorLevel === CLM . ReplayErrorLevel . BLOCKING || worstReplayError . errorLevel === CLM . ReplayErrorLevel . ERROR ) { return CLM . Validity . INVALID ; } if ( worstReplayError . errorLevel === CLM . ReplayErrorLevel . WARNING ) { return CLM . Validity . WARNING ; } } if ( trainDialog . validity === CLM . Validity . INVALID ) { return CLM . Validity . VALID ; } return trainDialog . validity ; } export function cleanTrainDialog ( trainDialog ) { for ( const round of trainDialog . rounds ) { if ( round . scorerSteps . length > 0 && round . scorerSteps [ 0 ] . labelAction === undefined ) { round . scorerSteps = [ ] ; } } for ( const round of trainDialog . rounds ) { for ( const scorerStep of round . scorerSteps ) { scorerStep . input . filledEntities = scorerStep . input . filledEntities . filter ( fe => fe . values . length > 0 ) ; } } } function doesScorerStepMatch ( scorerStep1 , scorerStep2 ) { if ( scorerStep1 . labelAction !== scorerStep2 . labelAction ) { return false ; } if ( scorerStep1 . input . filledEntities . length !== scorerStep2 . input . filledEntities . length ) { return false ; } let entityIndex = 0 ; while ( entityIndex < scorerStep1 . input . filledEntities . length ) { let entity1Id = scorerStep1 . input . filledEntities [ entityIndex ] . entityId ; if ( ! scorerStep2 . input . filledEntities . find ( fe => fe . entityId === entity1Id ) ) { return false ; } entityIndex = entityIndex + 1 ; } return true ; } export function hasInternalLabelConflict ( originalTrainDialog , newTrainDialog ) { if ( ! originalTrainDialog ) { return false ; } let originalExtractorSteps = originalTrainDialog . rounds . reduce ( ( acc , round ) => { return [ ... acc , ... round . extractorStep . textVariations ] ; } , [ ] ) ; let newExtractorSteps = newTrainDialog . rounds . reduce ( ( acc , round ) => { return [ ... acc , ... round . extractorStep . textVariations ] ; } , [ ] ) ; originalExtractorSteps = originalExtractorSteps . filter ( ( item , i , ar ) => ar . findIndex ( es => es . text === item . text ) === i ) ; newExtractorSteps = newExtractorSteps . filter ( ( item , i , ar ) => ar . findIndex ( es => es . text === item . text ) === i ) ; for ( let newVariation of newExtractorSteps ) { const sourceVariation = originalExtractorSteps . find ( tv => tv . text === newVariation . text ) ; if ( sourceVariation ) { if ( ! doLabelledEntitiesMatch ( newVariation . labelEntities , sourceVariation . labelEntities ) ) { return true ; } } } return false ; } function doLabelledEntitiesMatch ( labelEntities1 , labelEntities2 ) { const entityIds1 = labelEntities1 . map ( le => le . entityId ) . filter ( ( item , i , ar ) => ar . indexOf ( item ) === i ) ; const entityIds2 = labelEntities2 . map ( le => le . entityId ) . filter ( ( item , i , ar ) => ar . indexOf ( item ) === i ) ; if ( entityIds1 . length !== entityIds2 . length ) { return false ; } if ( entityIds1 . filter ( entityId => entityIds2 . indexOf ( entityId ) < 0 ) . length > 0 ) { return false ; } if ( entityIds2 . filter ( entityId => entityIds1 . indexOf ( entityId ) < 0 ) . length > 0 ) { return false ; } return true ; } function doesExtractorStepMatch ( extractorStep1 , extractorStep2 ) { const labelEntities1 = extractorStep1 . textVariations [ 0 ] . labelEntities ; const labelEntities2 = extractorStep2 . textVariations [ 0 ] . labelEntities ; return doLabelledEntitiesMatch ( labelEntities1 , labelEntities2 ) ; } function doesRoundMatch ( round1 , round2 , isLastRound ) { if ( ! doesExtractorStepMatch ( round1 . extractorStep , round2 . extractorStep ) ) { return false ; } if ( round1 . scorerSteps && ! round2 . scorerSteps || ! round1 . scorerSteps && round2 . scorerSteps ) { return isLastRound ; } if ( ! round1 . scorerSteps && ! round2 . scorerSteps ) { return true ; } const maxSteps = Math . max ( round1 . scorerSteps . length , round2 . scorerSteps . length ) ; let stepIndex = 0 ; while ( stepIndex < maxSteps ) { const scorerStep1 = round1 . scorerSteps [ stepIndex ] ; const scorerStep2 = round2 . scorerSteps [ stepIndex ] ; const extraScorerStep = ( scorerStep1 && ! scorerStep2 ) || ( ! scorerStep1 && scorerStep2 ) ; if ( extraScorerStep ) { return isLastRound ; } if ( ! doesScorerStepMatch ( scorerStep1 , scorerStep2 ) ) { return false ; } stepIndex = stepIndex + 1 ; } return true ; } export function doesTrainDialogMatch ( trainDialog1 , trainDialog2 ) { if ( trainDialog1 . trainDialogId === trainDialog2 . trainDialogId ) { return false ; } const maxRounds = Math . max ( trainDialog1 . rounds . length , trainDialog2 . rounds . length ) ; const minRounds = Math . min ( trainDialog1 . rounds . length , trainDialog2 . rounds . length ) ; let roundIndex = 0 ; while ( roundIndex < maxRounds ) { const round1 = trainDialog1 . rounds [ roundIndex ] ; const round2 = trainDialog2 . rounds [ roundIndex ] ; if ( ( round1 && ! round2 ) || ( round2 && ! round1 ) ) { return true ; } const isLastRound = ( roundIndex === minRounds - 1 ) ; if ( ! doesRoundMatch ( round1 , round2 , isLastRound ) ) { return false ; } roundIndex = roundIndex + 1 ; } return true ; } export function findMatchingTrainDialog ( trainDialog , trainDialogs , ignoreTrainDialogId = null ) { for ( const td of trainDialogs ) { if ( td . trainDialogId !== ignoreTrainDialogId && doesTrainDialogMatch ( trainDialog , td ) ) { return td ; } } return null ; } export function isPrimaryTrainDialog ( trainDialog1 , trainDialog2 ) { if ( ! trainDialog1 . trainDialogId ) { return false ; } if ( ! trainDialog2 . trainDialogId ) { return true ; } if ( trainDialog1 . rounds . length > trainDialog2 . rounds . length ) { return true ; } if ( trainDialog1 . rounds . length < trainDialog2 . rounds . length ) { return false ; } const lastRound1 = trainDialog1 . rounds [ trainDialog1 . rounds . length - 1 ] ; const lastRound2 = trainDialog2 . rounds [ trainDialog2 . rounds . length - 1 ] ; if ( lastRound1 . scorerSteps . length < lastRound2 . scorerSteps . length ) { return false ; } return true ; } export function mergeTrainDialogTags ( trainDialog1 , trainDialog2 ) { const dialog1Tags = [ ... trainDialog1 . tags ] ; const fromTagIndex = dialog1Tags . findIndex ( t => t === fromLogTag ) ; if ( fromTagIndex >= 0 ) { dialog1Tags . splice ( fromTagIndex , 1 ) ; } return [ ... new Set ( [ ... dialog1Tags , ... trainDialog2 . tags ] ) ] ; } export function mergeTrainDialogDescription ( trainDialog1 , trainDialog2 ) { return trainDialog1 . description . length > trainDialog2 . description . length ? trainDialog1 . description : trainDialog2 . description ; } export function mergeTrainDialogClientData ( trainDialog1 , trainDialog2 ) { const importHashes1 = trainDialog1 . clientData ? trainDialog1 . clientData . importHashes : [ ] ; const importHashes2 = trainDialog2 . clientData ? trainDialog2 . clientData . importHashes : [ ] ; return { importHashes : [ ... importHashes1 , ... importHashes2 ] . filter ( ( item , i , ar ) => ar . indexOf ( item ) === i ) } ; } export function mergeTrainDialogs ( trainDialog1 , trainDialog2 ) { if ( ! doesTrainDialogMatch ( trainDialog1 , trainDialog2 ) ) { throw new Error ( "Attempting to merge non-matching Train Dialogs" ) ; } const d1Longer = isPrimaryTrainDialog ( trainDialog1 , trainDialog2 ) ; const primaryTrainDialog = d1Longer ? trainDialog2 : trainDialog1 ; const mergedTrainDialog = deepCopy ( d1Longer ? trainDialog1 : trainDialog2 ) ; let roundIndex = 0 ; while ( ( roundIndex < primaryTrainDialog . rounds . length ) && ( roundIndex < mergedTrainDialog . rounds . length ) ) { const roundSmall = primaryTrainDialog . rounds [ roundIndex ] ; const roundLarge = mergedTrainDialog . rounds [ roundIndex ] ; const extractorStepSmall = roundSmall . extractorStep ; const extractorStepLarge = roundLarge . extractorStep ; const newTextVariations = extractorStepSmall . textVariations . filter ( tvs => ! extractorStepLarge . textVariations . find ( tvl => tvl . text === tvs . text ) ) ; roundLarge . extractorStep . textVariations = [ ... roundLarge . extractorStep . textVariations , ... newTextVariations ] . slice ( 0 , CLM . MAX_TEXT_VARIATIONS ) ; roundIndex = roundIndex + 1 ; } mergedTrainDialog . description = mergeTrainDialogDescription ( mergedTrainDialog , primaryTrainDialog ) ; mergedTrainDialog . tags = mergeTrainDialogTags ( mergedTrainDialog , primaryTrainDialog ) ; mergedTrainDialog . clientData = mergeTrainDialogClientData ( mergedTrainDialog , primaryTrainDialog ) ; return mergedTrainDialog ; } export function filledEntityIdMap ( filledEntities , entities ) { const filledEntityMap = CLM . FilledEntityMap . FromFilledEntities ( filledEntities , entities ) ; const filledIdMap = filledEntityMap . EntityMapToIdMap ( ) ; return CLM . getEntityDisplayValueMap ( filledIdMap ) ; } export function filledEntitiesToMemory ( filledEntities , entities ) { return filledEntities . map < CLM . Memory > ( fe => { const entity = entities . find ( e => e . entityId === fe . entityId ) ; const entityName = entity ? entity . entityName : 'UNKNOWN ENTITY' ; return { entityName : entityName , entityValues : fe . values } ; } ) ; } export function getPrevMemories ( trainDialog , entities , roundIndex , scoreIndex ) { let scorerStep = null ; if ( scoreIndex === null ) { const prevIndex = roundIndex - 1 ; if ( prevIndex >= 0 ) { const round = trainDialog . rounds [ prevIndex ] ; if ( round . scorerSteps . length > 0 ) { scorerStep = round . scorerSteps [ round . scorerSteps . length - 1 ] ; } } } else if ( scoreIndex === 0 ) { const round = trainDialog . rounds [ roundIndex ] ; scorerStep = round . scorerSteps [ 0 ] ; } else { const round = trainDialog . rounds [ roundIndex ] ; scorerStep = round . scorerSteps [ scoreIndex - 1 ] ; } if ( scorerStep ) { return filledEntitiesToMemory ( scorerStep . input . filledEntities , entities ) ; } return [ ] ; } export function getDialogRenderData ( trainDialog , entities , actions , roundIndex , scoreIndex , senderType ) { let scorerStep ; let scoreResponse ; let round ; let memories = [ ] ; let prevMemories = [ ] ; if ( roundIndex !== null && roundIndex < trainDialog . rounds . length ) { round = trainDialog . rounds [ roundIndex ] ; if ( round . scorerSteps . length > 0 ) { if ( typeof scoreIndex === "number" ) { scorerStep = round . scorerSteps [ scoreIndex ] ; if ( ! scorerStep ) { throw new Error ( `Cannot get score step at index: ${ scoreIndex } from array of length: ${ round . scorerSteps . length }` ) ; } let selectedAction = actions . find ( action => action . actionId === scorerStep ! . labelAction ) ; if ( ! selectedAction ) { selectedAction = { actionId : scorerStep . labelAction ?? 'MISSING ACTION' , createdDateTime : new Date ( ) . toJSON ( ) , payload : 'MISSING ACTION' , isTerminal : false , actionType : CLM . ActionTypes . TEXT , requiredEntitiesFromPayload : [ ] , requiredEntities : [ ] , requiredConditions : [ ] , negativeEntities : [ ] , negativeConditions : [ ] , suggestedEntity : undefined , version : 0 , packageCreationId : 0 , packageDeletionId : 0 , entityId : undefined , enumValueId : undefined , } ; } memories = filledEntitiesToMemory ( scorerStep . input . filledEntities , entities ) ; prevMemories = getPrevMemories ( trainDialog , entities , roundIndex , scoreIndex ) ; if ( scorerStep . uiScoreResponse ) { scoreResponse = scorerStep . uiScoreResponse ; } else { const scoredAction = { actionId : selectedAction . actionId , payload : selectedAction . payload , isTerminal : selectedAction . isTerminal , score : 1 , actionType : selectedAction . actionType } ; const unscoredActions = actions . filter ( a => ! selectedAction || a . actionId !== selectedAction . actionId ) . map < CLM . UnscoredAction > ( action => ( { actionId : action . actionId , payload : action . payload , isTerminal : action . isTerminal , reason : CLM . ScoreReason . NotCalculated , actionType : action . actionType } ) ) ; scoreResponse = { metrics : { wallTime : 0 } , scoredActions : [ scoredAction ] , unscoredActions : unscoredActions } ; } } else { memories = filledEntitiesToMemory ( round . scorerSteps [ 0 ] . input . filledEntities , entities ) ; prevMemories = getPrevMemories ( trainDialog , entities , roundIndex , scoreIndex ) ; } } else if ( scoreIndex === null ) { memories = getPrevMemories ( trainDialog , entities , roundIndex , scoreIndex ) ; prevMemories = memories ; } } return { dialogMode : ( senderType === CLM . SenderType . User ) ? CLM . DialogMode . Extractor : CLM . DialogMode . Scorer , scoreInput : scorerStep ? scorerStep . input : undefined , scoreResponse : scoreResponse , roundIndex , textVariations : round ? round . extractorStep . textVariations : [ ] , memories : filterDummyEntities ( memories ) , prevMemories : filterDummyEntities ( prevMemories ) , extractResponses : [ ] } ; } export function bestTemplateMatch ( importedAction , templates ) { let bestScore = 0 ; let bestTemplate = null ; for ( let template of templates ) { if ( template . body ) { const templateButtonCount = ( template . body . match ( /Action.Submit/g ) ?? [ ] ) . length ; if ( templateButtonCount === importedAction . buttons . length ) { const score = ( template ?. body ) ? compareTwoStrings ( importedAction . text , template . body ) : 0 ; if ( score > CARD_MATCH_THRESHOLD && score > bestScore ) { bestScore = score ; bestTemplate = template ; } else if ( ! bestTemplate && Util . isTemplateTitleGeneric ( template ) ) { if ( templateButtonCount === importedAction . buttons . length ) { bestTemplate = template ; } } } } } return bestTemplate ; }