feat: implement nlp based blocks prioritization strategy

feat: add weight to nlp entity schema and readapt

feat: remove commented obsolete code

feat: restore settings

feat: apply feedback

fix: re-adapt unit tests

feat: priority scoring re-calculation & enabling weight modification in builtin nlp entities

fix: remove obsolete code

feat: refine unit tests, apply mr coderabbit suggestions

fix: minor refactoring

feat: add nlp cache map type

feat: refine builtin nlp entities weight updates

feat: add more test cases and refine edge case handling

feat: add weight validation in UI

fix: apply feedback

feat: add a penalty factor & fix unit tests

feat: add documentation

fix: correct syntax

fix: remove stale log statement

fix: enforce nlp entity weight restrictions

fix: correct typo in docs

fix: typos in docs

fix: fix formatting for function comment

fix: restore matchNLP function previous code

fix: remove blank line, make updateOne asynchronous

fix: add AND operator in docs

fix: handle dependency injection in chat module

feat: refactor to use findAndPopulate in block score calculation

feat: refine caching mechanisms

feat: add typing and enforce safety checks

fix: remove typo

fix: remove async from block score calculation

fix: remove typo

fix: correct linting

fix: refine nlp pattern type check

fix: decompose code into helper utils,  add nlp entity dto validation, remove type casting

fix: minor refactoring

feat: refactor current implementation
This commit is contained in:
Mohamed Marrouchi
2025-03-26 13:11:07 +01:00
committed by Mohamed Marrouchi
parent 0db40680dc
commit bab2e3082f
31 changed files with 1061 additions and 49 deletions

View File

@@ -6,6 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import {
BadRequestException,
MethodNotAllowedException,
@@ -67,6 +68,12 @@ describe('NlpEntityController', () => {
NlpValueService,
NlpSampleEntityRepository,
NlpValueRepository,
{
provide: CACHE_MANAGER,
useValue: {
del: jest.fn(),
},
},
],
});
[nlpEntityController, nlpValueService, nlpEntityService] = await getMocks([
@@ -109,6 +116,7 @@ describe('NlpEntityController', () => {
) as NlpEntityFull['values'],
lookups: curr.lookups!,
builtin: curr.builtin!,
weight: curr.weight!,
});
return acc;
},
@@ -163,6 +171,7 @@ describe('NlpEntityController', () => {
name: 'sentiment',
lookups: ['trait'],
builtin: false,
weight: 1,
};
const result = await nlpEntityController.create(sentimentEntity);
expect(result).toEqualPayload(sentimentEntity);
@@ -214,6 +223,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 +248,7 @@ describe('NlpEntityController', () => {
doc: '',
lookups: ['trait'],
builtin: false,
weight: 1,
};
const result = await nlpEntityController.updateOne(
firstNameEntity!.id,
@@ -258,7 +269,7 @@ describe('NlpEntityController', () => {
).rejects.toThrow(NotFoundException);
});
it('should throw exception when nlp entity is builtin', async () => {
it('should throw an exception if entity is builtin but weight not provided', async () => {
const updateNlpEntity: NlpEntityCreateDto = {
name: 'updated',
doc: '',
@@ -269,6 +280,57 @@ describe('NlpEntityController', () => {
nlpEntityController.updateOne(buitInEntityId!, updateNlpEntity),
).rejects.toThrow(MethodNotAllowedException);
});
it('should update weight if entity is builtin and weight is provided', async () => {
const updatedNlpEntity: NlpEntityCreateDto = {
name: 'updated',
doc: '',
lookups: ['trait'],
builtin: false,
weight: 4,
};
const findOneSpy = jest.spyOn(nlpEntityService, 'findOne');
const updateWeightSpy = jest.spyOn(nlpEntityService, 'updateWeight');
const result = await nlpEntityController.updateOne(
buitInEntityId!,
updatedNlpEntity,
);
expect(findOneSpy).toHaveBeenCalledWith(buitInEntityId!);
expect(updateWeightSpy).toHaveBeenCalledWith(
buitInEntityId!,
updatedNlpEntity.weight,
);
expect(result.weight).toBe(updatedNlpEntity.weight);
});
it('should update only the weight of the builtin entity', async () => {
const updatedNlpEntity: NlpEntityCreateDto = {
name: 'updated',
doc: '',
lookups: ['trait'],
builtin: false,
weight: 4,
};
const originalEntity: NlpEntity | null = await nlpEntityService.findOne(
buitInEntityId!,
);
const result: NlpEntity = await nlpEntityController.updateOne(
buitInEntityId!,
updatedNlpEntity,
);
// Check weight is updated
expect(result.weight).toBe(updatedNlpEntity.weight);
Object.entries(originalEntity!).forEach(([key, value]) => {
if (key !== 'weight' && key !== 'updatedAt') {
expect(result[key as keyof typeof result]).toEqual(value);
}
});
});
});
describe('deleteMany', () => {
it('should delete multiple nlp entities', async () => {

View File

@@ -157,10 +157,19 @@ export class NlpEntityController extends BaseController<
this.logger.warn(`Unable to update NLP Entity by id ${id}`);
throw new NotFoundException(`NLP Entity with ID ${id} not found`);
}
if (nlpEntity.builtin) {
throw new MethodNotAllowedException(
`Cannot update builtin NLP Entity ${nlpEntity.name}`,
);
// Only allow weight update for builtin entities
if (updateNlpEntityDto.weight) {
return await this.nlpEntityService.updateWeight(
id,
updateNlpEntityDto.weight,
);
} else {
throw new MethodNotAllowedException(
`Cannot update builtin NLP Entity ${nlpEntity.name} except for weight`,
);
}
}
return await this.nlpEntityService.updateOne(id, updateNlpEntityDto);

View File

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

View File

@@ -6,6 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { BadRequestException, NotFoundException } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@@ -57,6 +58,12 @@ describe('NlpValueController', () => {
NlpSampleEntityRepository,
NlpEntityService,
NlpEntityRepository,
{
provide: CACHE_MANAGER,
useValue: {
del: jest.fn(),
},
},
],
});
[nlpValueController, nlpValueService, nlpEntityService] = await getMocks([