mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: implement nlp based blocks prioritization strategy
This commit is contained in:
parent
c121ce7c02
commit
089486969c
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2025 Hexastack. All rights reserved.
|
||||
* Copyright © 2025 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
@ -11,9 +12,6 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
|
||||
import { AttachmentModule } from '@/attachment/attachment.module';
|
||||
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
|
||||
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { ChannelModule } from '@/channel/channel.module';
|
||||
import { CmsModule } from '@/cms/cms.module';
|
||||
import { NlpModule } from '@/nlp/nlp.module';
|
||||
@ -62,7 +60,6 @@ import { SubscriberService } from './services/subscriber.service';
|
||||
SubscriberModel,
|
||||
ConversationModel,
|
||||
SubscriberModel,
|
||||
AttachmentModel,
|
||||
]),
|
||||
forwardRef(() => ChannelModule),
|
||||
CmsModule,
|
||||
@ -98,8 +95,6 @@ import { SubscriberService } from './services/subscriber.service';
|
||||
ConversationService,
|
||||
ChatService,
|
||||
BotService,
|
||||
AttachmentService,
|
||||
AttachmentRepository,
|
||||
],
|
||||
exports: [
|
||||
SubscriberService,
|
||||
|
@ -37,7 +37,6 @@ import { NlpValueRepository } from '@/nlp/repositories/nlp-value.repository';
|
||||
import { NlpEntityModel } from '@/nlp/schemas/nlp-entity.schema';
|
||||
import { NlpSampleEntityModel } from '@/nlp/schemas/nlp-sample-entity.schema';
|
||||
import { NlpValueModel } from '@/nlp/schemas/nlp-value.schema';
|
||||
import { NlpCacheMap } from '@/nlp/schemas/types';
|
||||
import { NlpEntityService } from '@/nlp/services/nlp-entity.service';
|
||||
import { NlpValueService } from '@/nlp/services/nlp-value.service';
|
||||
import { PluginService } from '@/plugins/plugins.service';
|
||||
@ -52,11 +51,8 @@ import {
|
||||
blockGetStarted,
|
||||
blockProductListMock,
|
||||
blocks,
|
||||
mockModifiedNlpBlock,
|
||||
mockNlpBlock,
|
||||
mockNlpPatternsSetOne,
|
||||
mockNlpPatternsSetThree,
|
||||
mockNlpPatternsSetTwo,
|
||||
nlpBlocks,
|
||||
} from '@/utils/test/mocks/block';
|
||||
import {
|
||||
contextBlankInstance,
|
||||
@ -85,6 +81,25 @@ import { CategoryRepository } from './../repositories/category.repository';
|
||||
import { BlockService } from './block.service';
|
||||
import { CategoryService } from './category.service';
|
||||
|
||||
// Create a mock for the NlpEntityService
|
||||
const mockNlpEntityService = {
|
||||
findOne: jest.fn().mockImplementation((query) => {
|
||||
if (query.name === 'intent') {
|
||||
return Promise.resolve({
|
||||
lookups: ['trait'],
|
||||
id: '67e3e41eff551ca5be70559c',
|
||||
});
|
||||
}
|
||||
if (query.name === 'firstname') {
|
||||
return Promise.resolve({
|
||||
lookups: ['trait'],
|
||||
id: '67e3e41eff551ca5be70559d',
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null); // Default response if the entity isn't found
|
||||
}),
|
||||
};
|
||||
|
||||
describe('BlockService', () => {
|
||||
let blockRepository: BlockRepository;
|
||||
let categoryRepository: CategoryRepository;
|
||||
@ -95,6 +110,8 @@ describe('BlockService', () => {
|
||||
let contentService: ContentService;
|
||||
let contentTypeService: ContentTypeService;
|
||||
let nlpEntityService: NlpEntityService;
|
||||
let settingService: SettingService;
|
||||
let settings: Settings;
|
||||
|
||||
beforeAll(async () => {
|
||||
const { getMocks } = await buildTestingMocks({
|
||||
@ -112,8 +129,8 @@ describe('BlockService', () => {
|
||||
LabelModel,
|
||||
LanguageModel,
|
||||
NlpEntityModel,
|
||||
NlpSampleEntityModel,
|
||||
NlpValueModel,
|
||||
NlpSampleEntityModel,
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
@ -123,19 +140,19 @@ describe('BlockService', () => {
|
||||
ContentRepository,
|
||||
AttachmentRepository,
|
||||
LanguageRepository,
|
||||
NlpEntityRepository,
|
||||
NlpSampleEntityRepository,
|
||||
NlpValueRepository,
|
||||
BlockService,
|
||||
CategoryService,
|
||||
ContentTypeService,
|
||||
ContentService,
|
||||
AttachmentService,
|
||||
LanguageService,
|
||||
NlpEntityRepository,
|
||||
NlpValueRepository,
|
||||
NlpSampleEntityRepository,
|
||||
NlpEntityService,
|
||||
NlpValueService,
|
||||
{
|
||||
provide: NlpValueService,
|
||||
useValue: {},
|
||||
provide: NlpEntityService, // Mocking NlpEntityService
|
||||
useValue: mockNlpEntityService,
|
||||
},
|
||||
{
|
||||
provide: PluginService,
|
||||
@ -169,22 +186,14 @@ describe('BlockService', () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
[
|
||||
blockService,
|
||||
contentService,
|
||||
contentTypeService,
|
||||
categoryRepository,
|
||||
blockRepository,
|
||||
nlpEntityService,
|
||||
] = await getMocks([
|
||||
BlockService,
|
||||
ContentService,
|
||||
ContentTypeService,
|
||||
CategoryRepository,
|
||||
BlockRepository,
|
||||
NlpEntityService,
|
||||
]);
|
||||
}).compile();
|
||||
blockService = module.get<BlockService>(BlockService);
|
||||
contentService = module.get<ContentService>(ContentService);
|
||||
settingService = module.get<SettingService>(SettingService);
|
||||
contentTypeService = module.get<ContentTypeService>(ContentTypeService);
|
||||
categoryRepository = module.get<CategoryRepository>(CategoryRepository);
|
||||
blockRepository = module.get<BlockRepository>(BlockRepository);
|
||||
nlpEntityService = module.get<NlpEntityService>(NlpEntityService);
|
||||
category = (await categoryRepository.findOne({ label: 'default' }))!;
|
||||
hasPreviousBlocks = (await blockRepository.findOne({
|
||||
name: 'hasPreviousBlocks',
|
||||
@ -545,6 +554,59 @@ describe('BlockService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchBestNLP', () => {
|
||||
it('should return undefined if blocks is empty', async () => {
|
||||
const result = await blockService.matchBestNLP([]);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the only block if there is one', async () => {
|
||||
const result = await blockService.matchBestNLP([blockEmpty]);
|
||||
expect(result).toBe(blockEmpty);
|
||||
});
|
||||
|
||||
it('should correctly select the best block based on NLP scores', async () => {
|
||||
const result = await blockService.matchBestNLP(nlpBlocks);
|
||||
expect(result).toBe(mockNlpBlock);
|
||||
|
||||
// Iterate over each block
|
||||
for (const block of nlpBlocks) {
|
||||
// Flatten the patterns array and filter valid NLP patterns
|
||||
block.patterns
|
||||
.flatMap((pattern) => (Array.isArray(pattern) ? pattern : []))
|
||||
.filter((p) => typeof p === 'object' && 'entity' in p && 'match' in p) // Filter only valid patterns with entity and match
|
||||
.forEach((p) => {
|
||||
// Check if findOne was called with the correct entity
|
||||
expect(nlpEntityService.findOne).toHaveBeenCalledWith(
|
||||
{ name: p.entity },
|
||||
undefined,
|
||||
{ _id: 0, lookups: 1 },
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the block with the highest combined score', async () => {
|
||||
const result = await blockService.matchBestNLP(nlpBlocks);
|
||||
expect(result).toBe(mockNlpBlock);
|
||||
// Iterate over each block
|
||||
for (const block of nlpBlocks) {
|
||||
// Flatten the patterns array and filter valid NLP patterns
|
||||
block.patterns
|
||||
.flatMap((pattern) => (Array.isArray(pattern) ? pattern : []))
|
||||
.filter((p) => typeof p === 'object' && 'entity' in p && 'match' in p) // Filter only valid patterns with entity and match
|
||||
.forEach((p) => {
|
||||
// Check if findOne was called with the correct entity
|
||||
expect(nlpEntityService.findOne).toHaveBeenCalledWith(
|
||||
{ name: p.entity },
|
||||
undefined,
|
||||
{ _id: 0, lookups: 1 },
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchText', () => {
|
||||
it('should return false for matching an empty text', () => {
|
||||
const result = blockService.matchText('', blockGetStarted);
|
||||
|
@ -16,7 +16,6 @@ import { CONSOLE_CHANNEL_NAME } from '@/extensions/channels/console/settings';
|
||||
import { NLU } from '@/helper/types';
|
||||
import { I18nService } from '@/i18n/services/i18n.service';
|
||||
import { LanguageService } from '@/i18n/services/language.service';
|
||||
import { NlpCacheMap } from '@/nlp/schemas/types';
|
||||
import { NlpEntityService } from '@/nlp/services/nlp-entity.service';
|
||||
import { PluginService } from '@/plugins/plugins.service';
|
||||
import { PluginType } from '@/plugins/types';
|
||||
@ -38,10 +37,8 @@ import {
|
||||
StdOutgoingSystemEnvelope,
|
||||
} from '../schemas/types/message';
|
||||
import {
|
||||
isNlpPattern,
|
||||
NlpPattern,
|
||||
NlpPatternMatchResult,
|
||||
PayloadPattern,
|
||||
PayloadPattern
|
||||
} from '../schemas/types/pattern';
|
||||
import { Payload, StdQuickReply } from '../schemas/types/quick-reply';
|
||||
import { SubscriberContext } from '../schemas/types/subscriberContext';
|
||||
@ -191,41 +188,11 @@ export class BlockService extends BaseService<
|
||||
// Perform an NLP Match
|
||||
|
||||
if (!block && nlp) {
|
||||
// Use the `reduce` function to iterate over `filteredBlocks` and accumulate a new array `matchesWithPatterns`.
|
||||
// This approach combines the matching of NLP patterns and filtering of blocks with empty or invalid matches
|
||||
// into a single operation. This avoids the need for a separate mapping and filtering step, improving performance.
|
||||
// For each block in `filteredBlocks`, we call `matchNLP` to find patterns that match the NLP data.
|
||||
// If `matchNLP` returns a non-empty list of matched patterns, the block and its matched patterns are added
|
||||
// to the accumulator array `acc`, which is returned as the final result.
|
||||
// This ensures that only blocks with valid matches are kept, and blocks with no matches are excluded,
|
||||
// all while iterating through the list only once.
|
||||
|
||||
const matchesWithPatterns: NlpPatternMatchResult[] =
|
||||
filteredBlocks.reduce<NlpPatternMatchResult[]>((acc, b) => {
|
||||
const matchedPattern = this.matchNLP(nlp, b);
|
||||
|
||||
if (matchedPattern && matchedPattern.length > 0) {
|
||||
acc.push({ block: b, matchedPattern });
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// @TODO Make nluPenaltyFactor configurable in UI settings
|
||||
const nluPenaltyFactor = 0.95;
|
||||
// Log the matched patterns
|
||||
this.logger.debug(
|
||||
`Matched patterns: ${JSON.stringify(matchesWithPatterns.map((p) => p.matchedPattern))}`,
|
||||
);
|
||||
|
||||
// Proceed with matching the best NLP block
|
||||
if (matchesWithPatterns.length > 0) {
|
||||
block = (await this.matchBestNLP(
|
||||
matchesWithPatterns.map((m) => m.block),
|
||||
matchesWithPatterns.map((p) => p.matchedPattern),
|
||||
nlp,
|
||||
nluPenaltyFactor,
|
||||
)) as BlockFull | undefined;
|
||||
}
|
||||
// Find block pattern having the best match of nlp entities
|
||||
const newBlocks = filteredBlocks.filter((b) => {
|
||||
return this.matchNLP(nlp, b);
|
||||
});
|
||||
block = (await this.matchBestNLP(newBlocks)) as BlockFull | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,109 +338,80 @@ export class BlockService extends BaseService<
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the best-matching block based on NLP pattern scoring.
|
||||
* Identifies and returns the best-matching block based on NLP entity scores.
|
||||
*
|
||||
* This function evaluates each block by calculating a score derived from its matched NLP patterns,
|
||||
* the parsed NLP entities, and a penalty factor. It compares the scores across all blocks and
|
||||
* returns the one with the highest calculated score.
|
||||
* This function evaluates a list of blocks by analyzing their associated NLP entities
|
||||
* and scoring them based on predefined lookup entities. The block with the highest
|
||||
* score is selected as the best match.
|
||||
* @param blocks - Blocks on which to perform the filtering
|
||||
*
|
||||
* @param blocks - An array of candidate blocks to evaluate.
|
||||
* @param matchedPatterns - A two-dimensional array of matched NLP patterns corresponding to each block.
|
||||
* @param nlp - The parsed NLP entities used for scoring.
|
||||
* @param nlpPenaltyFactor - A numeric penalty factor applied during scoring to influence block selection.
|
||||
* @returns The block with the highest NLP score, or undefined if no valid block is found.
|
||||
* @returns The best block
|
||||
*/
|
||||
async matchBestNLP(
|
||||
blocks: (Block | BlockFull)[] | undefined,
|
||||
matchedPatterns: NlpPattern[][],
|
||||
nlp: NLU.ParseEntities,
|
||||
nlpPenaltyFactor: number,
|
||||
blocks: Block[] | BlockFull[] | undefined,
|
||||
): Promise<Block | BlockFull | undefined> {
|
||||
if (!blocks || blocks.length === 0) return undefined;
|
||||
if (blocks.length === 1) return blocks[0];
|
||||
// @TODO make lookup scores configurable in hexabot settings
|
||||
const lookupScores: { [key: string]: number } = {
|
||||
trait: 2,
|
||||
keywords: 1,
|
||||
};
|
||||
|
||||
// No blocks to check against
|
||||
if (blocks?.length === 0 || !blocks) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If there's only one block, return it immediately.
|
||||
if (blocks.length === 1) {
|
||||
return blocks[0];
|
||||
}
|
||||
let bestBlock: Block | BlockFull | undefined;
|
||||
let highestScore = 0;
|
||||
const entityNames: string[] = blocks.flatMap((block) =>
|
||||
block.patterns.flatMap((patternGroup) => {
|
||||
if (Array.isArray(patternGroup)) {
|
||||
return patternGroup.flatMap((pattern) =>
|
||||
isNlpPattern(pattern) ? [pattern.entity] : [],
|
||||
);
|
||||
}
|
||||
return []; // Skip non-array patternGroups
|
||||
}),
|
||||
);
|
||||
const uniqueEntityNames: string[] = [...new Set(entityNames)];
|
||||
const nlpCacheMap: NlpCacheMap =
|
||||
await this.entityService.getNlpMap(uniqueEntityNames);
|
||||
// Iterate through all blocks and calculate their NLP score
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
const block = blocks[i];
|
||||
const patterns = matchedPatterns[i];
|
||||
// If compatible, calculate the NLP score for this block
|
||||
const nlpScore: number = this.calculateBlockScore(
|
||||
patterns,
|
||||
nlp,
|
||||
nlpCacheMap,
|
||||
nlpPenaltyFactor,
|
||||
|
||||
// Iterate over each block in blocks
|
||||
for (const block of blocks) {
|
||||
let nlpScore = 0;
|
||||
|
||||
// Gather all entity lookups for patterns that include an entity
|
||||
const entityLookups = await Promise.all(
|
||||
block.patterns
|
||||
.flatMap((pattern) => (Array.isArray(pattern) ? pattern : []))
|
||||
.filter((p) => typeof p === 'object' && 'entity' in p && 'match' in p)
|
||||
.map(async (pattern) => {
|
||||
const entityName = pattern.entity;
|
||||
return await this.entityService.findOne(
|
||||
{ name: entityName },
|
||||
undefined,
|
||||
{ lookups: 1, _id: 0 },
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
nlpScore += entityLookups.reduce((score, entityLookup) => {
|
||||
if (
|
||||
entityLookup &&
|
||||
entityLookup.lookups[0] &&
|
||||
lookupScores[entityLookup.lookups[0]]
|
||||
) {
|
||||
return score + lookupScores[entityLookup.lookups[0]]; // Add points based on the lookup type
|
||||
}
|
||||
return score; // Return the current score if no match
|
||||
}, 0);
|
||||
|
||||
// Update the best block if the current block has a higher NLP score
|
||||
if (nlpScore > highestScore) {
|
||||
highestScore = nlpScore;
|
||||
bestBlock = block;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(`Best NLP score obtained: ${highestScore}`);
|
||||
this.logger.debug(`Best block selected: ${JSON.stringify(bestBlock)}`);
|
||||
|
||||
this.logger.debug(`Best Nlp Score obtained ${highestScore}`);
|
||||
this.logger.debug(
|
||||
`Best retrieved block based on NLP entities ${JSON.stringify(bestBlock)}`,
|
||||
);
|
||||
return bestBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the NLP score for a given block using its matched NLP patterns and parsed NLP entities.
|
||||
*
|
||||
* Each pattern is evaluated against the parsed NLP entities to determine matches based on entity name,
|
||||
* value, and confidence. A score is computed using the entity's weight and the confidence level of the match.
|
||||
* A penalty factor is optionally applied for entity-level matches to adjust the scoring.
|
||||
*
|
||||
* The function uses a cache (`nlpCacheMap`) to avoid redundant database lookups for entity metadata.
|
||||
*
|
||||
* @param patterns - The NLP patterns associated with the block.
|
||||
* @param nlp - The parsed NLP entities from the user input.
|
||||
* @param nlpCacheMap - A cache to reuse fetched entity metadata (e.g., weights and valid values).
|
||||
* @param nlpPenaltyFactor - A multiplier applied to scores when the pattern match type is 'entity'.
|
||||
* @returns A numeric score representing how well the block matches the given NLP context.
|
||||
*/
|
||||
calculateBlockScore(
|
||||
patterns: NlpPattern[],
|
||||
nlp: NLU.ParseEntities,
|
||||
nlpCacheMap: NlpCacheMap,
|
||||
nlpPenaltyFactor: number,
|
||||
): number {
|
||||
// Compute individual pattern scores using the cache
|
||||
const patternScores: number[] = patterns.map((pattern) => {
|
||||
const entityData = nlpCacheMap.get(pattern.entity);
|
||||
if (!entityData) return 0;
|
||||
|
||||
const matchedEntity: NLU.ParseEntity | undefined = nlp.entities.find(
|
||||
(e) =>
|
||||
e.entity === pattern.entity &&
|
||||
entityData?.values.some((v) => v === e.value) &&
|
||||
(pattern.match !== 'value' || e.value === pattern.value),
|
||||
);
|
||||
|
||||
return matchedEntity?.confidence
|
||||
? matchedEntity.confidence *
|
||||
entityData.weight *
|
||||
(pattern.match === 'entity' ? nlpPenaltyFactor : 1)
|
||||
: 0;
|
||||
});
|
||||
|
||||
// Sum the scores
|
||||
return patternScores.reduce((sum, score) => sum + score, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches an outcome-based block from a list of available blocks
|
||||
* based on the outcome of a system message.
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2025 Hexastack. All rights reserved.
|
||||
* Copyright © 2025 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
|
@ -31,6 +31,7 @@ import { NlpSampleService } from './services/nlp-sample.service';
|
||||
import { NlpValueService } from './services/nlp-value.service';
|
||||
import { NlpService } from './services/nlp.service';
|
||||
|
||||
// @Global()
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([
|
||||
|
@ -16,7 +16,7 @@ import { ButtonType, PayloadType } from '@/chat/schemas/types/button';
|
||||
import { CaptureVar } from '@/chat/schemas/types/capture-var';
|
||||
import { OutgoingMessageFormat } from '@/chat/schemas/types/message';
|
||||
import { BlockOptions, ContentOptions } from '@/chat/schemas/types/options';
|
||||
import { NlpPattern, Pattern } from '@/chat/schemas/types/pattern';
|
||||
import { Pattern } from '@/chat/schemas/types/pattern';
|
||||
import { QuickReplyType } from '@/chat/schemas/types/quick-reply';
|
||||
|
||||
import { modelInstance } from './misc';
|
||||
@ -246,45 +246,7 @@ export const blockGetStarted = {
|
||||
message: ['Welcome! How are you ? '],
|
||||
} as unknown as BlockFull;
|
||||
|
||||
export const mockNlpPatternsSetOne: NlpPattern[] = [
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'greeting',
|
||||
},
|
||||
{
|
||||
entity: 'firstname',
|
||||
match: 'value',
|
||||
value: 'jhon',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockNlpPatternsSetTwo: NlpPattern[] = [
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'affirmation',
|
||||
},
|
||||
{
|
||||
entity: 'firstname',
|
||||
match: 'value',
|
||||
value: 'mark',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockNlpPatternsSetThree: NlpPattern[] = [
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'greeting',
|
||||
},
|
||||
{
|
||||
entity: 'firstname',
|
||||
match: 'entity',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockNlpBlock: BlockFull = {
|
||||
export const mockNlpBlock = {
|
||||
...baseBlockInstance,
|
||||
name: 'Mock Nlp',
|
||||
patterns: [
|
||||
@ -292,38 +254,27 @@ export const mockNlpBlock: BlockFull = {
|
||||
'/we*lcome/',
|
||||
{ label: 'Mock Nlp', value: 'MOCK_NLP' },
|
||||
[
|
||||
...mockNlpPatternsSetOne,
|
||||
[
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'greeting',
|
||||
},
|
||||
{
|
||||
entity: 'firstname',
|
||||
match: 'value',
|
||||
value: 'doe',
|
||||
},
|
||||
],
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'greeting',
|
||||
},
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'want',
|
||||
},
|
||||
{
|
||||
entity: 'intent',
|
||||
match: 'value',
|
||||
value: 'affirmative',
|
||||
},
|
||||
],
|
||||
],
|
||||
trigger_labels: customerLabelsMock,
|
||||
message: ['Good to see you again '],
|
||||
} as unknown as BlockFull;
|
||||
|
||||
export const mockModifiedNlpBlock: BlockFull = {
|
||||
...baseBlockInstance,
|
||||
name: 'Modified Mock Nlp',
|
||||
patterns: [
|
||||
'Hello',
|
||||
'/we*lcome/',
|
||||
{ label: 'Modified Mock Nlp', value: 'MODIFIED_MOCK_NLP' },
|
||||
[...mockNlpPatternsSetThree],
|
||||
],
|
||||
trigger_labels: customerLabelsMock,
|
||||
message: ['Hello there'],
|
||||
} as unknown as BlockFull;
|
||||
|
||||
const patternsProduct: Pattern[] = [
|
||||
'produit',
|
||||
[
|
||||
@ -365,3 +316,5 @@ export const blockCarouselMock = {
|
||||
export const blocks: BlockFull[] = [blockGetStarted, blockEmpty];
|
||||
|
||||
export const nlpBlocks: BlockFull[] = [blockGetStarted, mockNlpBlock];
|
||||
|
||||
export const nlpBlocks: BlockFull[] = [blockGetStarted, mockNlpBlock];
|
||||
|
Loading…
Reference in New Issue
Block a user