feat: add weight to nlp entity schema and readapt

This commit is contained in:
Mohamed Marrouchi 2025-04-02 11:57:57 +01:00 committed by MohamedAliBouhaouala
parent 95e07c84bc
commit 67ce8ebbfc
14 changed files with 61 additions and 20 deletions

View File

@ -83,12 +83,14 @@ const mockNlpEntityService = {
return Promise.resolve({
lookups: ['trait'],
id: '67e3e41eff551ca5be70559c',
weight: 1,
});
}
if (query.name === 'firstname') {
return Promise.resolve({
lookups: ['trait'],
id: '67e3e41eff551ca5be70559d',
weight: 1,
});
}
return Promise.resolve(null); // Default response if the entity isn't found
@ -444,7 +446,7 @@ describe('BlockService', () => {
expect(nlpEntityService.findOne).toHaveBeenCalledWith(
{ name: p.entity },
undefined,
{ _id: 0, lookups: 1 },
{ _id: 0, lookups: 1, weight: 1 },
);
});
}
@ -464,7 +466,7 @@ describe('BlockService', () => {
expect(nlpEntityService.findOne).toHaveBeenCalledWith(
{ name: p.entity },
undefined,
{ _id: 0, lookups: 1 },
{ _id: 0, lookups: 1, weight: 1 },
);
});
}

View File

@ -348,12 +348,6 @@ export class BlockService extends BaseService<
async matchBestNLP(
blocks: Block[] | BlockFull[] | undefined,
): Promise<Block | BlockFull | undefined> {
// @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;
@ -380,18 +374,14 @@ export class BlockService extends BaseService<
return await this.entityService.findOne(
{ name: entityName },
undefined,
{ lookups: 1, _id: 0 },
{ lookups: 1, weight: 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
if (entityLookup && entityLookup.lookups[0] && entityLookup.weight) {
return score + entityLookup.weight; // Add points based on the Nlp entity associated weight
}
return score; // Return the current score if no match
}, 0);

View File

@ -41,7 +41,7 @@ export default [
{
group: WEB_CHANNEL_NAMESPACE,
label: 'greeting_message',
value: 'Welcome! Ready to start a conversation with our chatbot?',
value: '',
type: SettingType.textarea,
translatable: true,
},

View File

@ -139,6 +139,7 @@ describe('BaseNlpHelper', () => {
updatedAt: new Date(),
builtin: false,
lookups: [],
weight: 1,
},
entity2: {
id: new ObjectId().toString(),
@ -147,6 +148,7 @@ describe('BaseNlpHelper', () => {
updatedAt: new Date(),
builtin: false,
lookups: [],
weight: 1,
},
});
jest.spyOn(NlpValue, 'getValueMap').mockReturnValue({
@ -207,6 +209,7 @@ describe('BaseNlpHelper', () => {
updatedAt: new Date(),
builtin: false,
lookups: [],
weight: 1,
},
});

View File

@ -109,6 +109,7 @@ describe('NlpEntityController', () => {
) as NlpEntityFull['values'],
lookups: curr.lookups!,
builtin: curr.builtin!,
weight: curr.weight!,
});
return acc;
},
@ -163,6 +164,7 @@ describe('NlpEntityController', () => {
name: 'sentiment',
lookups: ['trait'],
builtin: false,
weight: 1,
};
const result = await nlpEntityController.create(sentimentEntity);
expect(result).toEqualPayload(sentimentEntity);
@ -214,6 +216,7 @@ describe('NlpEntityController', () => {
updatedAt: firstNameEntity!.updatedAt,
lookups: firstNameEntity!.lookups,
builtin: firstNameEntity!.builtin,
weight: firstNameEntity!.weight,
};
const result = await nlpEntityController.findOne(firstNameEntity!.id, [
'values',
@ -238,6 +241,7 @@ describe('NlpEntityController', () => {
doc: '',
lookups: ['trait'],
builtin: false,
weight: 1,
};
const result = await nlpEntityController.updateOne(
firstNameEntity!.id,

View File

@ -372,6 +372,7 @@ describe('NlpSampleController', () => {
lookups: ['trait'],
doc: '',
builtin: false,
weight: 1,
};
const priceValueEntity = await nlpEntityService.findOne({
name: 'intent',

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 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:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -12,6 +12,7 @@ import {
IsBoolean,
IsIn,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
Matches,
@ -47,6 +48,14 @@ export class NlpEntityCreateDto {
@IsBoolean()
@IsOptional()
builtin?: boolean;
@ApiPropertyOptional({
description: 'Nlp entity associated weight for next block triggering',
type: Number,
})
@IsNumber()
@IsOptional()
weight?: number;
}
export type NlpEntityDto = DtoConfig<{

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 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:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -58,6 +58,12 @@ export class NlpEntityStub extends BaseSchema {
@Prop({ type: Boolean, default: false })
builtin: boolean;
/**
* Entity's weight used to determine the next block to trigger in the conversational flow.
*/
@Prop({ type: Number, default: 1, unique: false })
weight: number;
/**
* Returns a map object for entities
* @param entities - Array of entities

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 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:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -17,18 +17,21 @@ export const nlpEntityFixtures: NlpEntityCreateDto[] = [
lookups: ['trait'],
doc: '',
builtin: false,
weight: 1,
},
{
name: 'first_name',
lookups: ['keywords'],
doc: '',
builtin: false,
weight: 1,
},
{
name: 'built_in',
lookups: ['trait'],
doc: '',
builtin: true,
weight: 1,
},
];

View File

@ -348,6 +348,7 @@
"nlp_lookup_trait": "Trait",
"doc": "Documentation",
"builtin": "Built-in?",
"weight": "Weight",
"dataset": "Dataset",
"yes": "Yes",
"no": "No",

View File

@ -347,6 +347,7 @@
"nlp_lookup_trait": "Trait",
"synonyms": "Synonymes",
"doc": "Documentation",
"weight": "Poids",
"builtin": "Intégré?",
"dataset": "Données",
"yes": "Oui",

View File

@ -165,6 +165,16 @@ const NlpEntity = () => {
resizable: false,
renderHeader,
},
{
maxWidth: 210,
field: "weight",
headerName: t("label.weight"),
renderCell: (val) => <Chip label={val.value} variant="title" />,
sortable: true,
disableColumnMenu: true,
resizable: false,
renderHeader,
},
{
maxWidth: 90,
field: "builtin",

View File

@ -60,6 +60,7 @@ export const NlpEntityVarForm: FC<ComponentFormProps<INlpEntity>> = ({
name: data?.name || "",
doc: data?.doc || "",
lookups: data?.lookups || ["keywords"],
weight: data?.weight || 1,
},
});
const validationRules = {
@ -82,6 +83,7 @@ export const NlpEntityVarForm: FC<ComponentFormProps<INlpEntity>> = ({
reset({
name: data.name,
doc: data.doc,
weight: data.weight,
});
} else {
reset();
@ -130,6 +132,14 @@ export const NlpEntityVarForm: FC<ComponentFormProps<INlpEntity>> = ({
multiline={true}
/>
</ContentItem>
<ContentItem>
<Input
label={t("label.weight")}
{...register("weight", { valueAsNumber: true })}
type="number"
inputProps={{ min: 1, step: 1 }} // Restricts input to positive integers only
/>
</ContentItem>
</ContentContainer>
</form>
</Wrapper>

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 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:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -19,6 +19,7 @@ export interface INlpEntityAttributes {
lookups: Lookup[];
doc?: string;
builtin?: boolean;
weight: number;
}
export enum NlpLookups {