mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
Merge branch 'main' into fix/channel-data-inference-type-issue
This commit is contained in:
@@ -94,7 +94,7 @@ export class AttachmentController extends BaseController<Attachment> {
|
||||
)
|
||||
filters: TFilterQuery<Attachment>,
|
||||
) {
|
||||
return await this.attachmentService.findPage(filters, pageQuery);
|
||||
return await this.attachmentService.find(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +153,7 @@ export class AttachmentController extends BaseController<Attachment> {
|
||||
throw new NotFoundException('Attachment not found');
|
||||
}
|
||||
|
||||
return this.attachmentService.download(attachment);
|
||||
return await this.attachmentService.download(attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
StdIncomingMessage,
|
||||
} from '@/chat/schemas/types/message';
|
||||
import { Payload } from '@/chat/schemas/types/quick-reply';
|
||||
import { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
|
||||
import ChannelHandler, { ChannelNameOf } from './Handler';
|
||||
|
||||
@@ -38,7 +38,7 @@ export default abstract class EventWrapper<
|
||||
|
||||
_profile!: Subscriber;
|
||||
|
||||
_nlp!: Nlp.ParseEntities;
|
||||
_nlp!: NLU.ParseEntities;
|
||||
|
||||
/**
|
||||
* Constructor : Class used to wrap any channel's event in order
|
||||
@@ -148,7 +148,7 @@ export default abstract class EventWrapper<
|
||||
*
|
||||
* @returns The parsed NLP entities, or null if not available.
|
||||
*/
|
||||
getNLP(): Nlp.ParseEntities | null {
|
||||
getNLP(): NLU.ParseEntities | null {
|
||||
return this._nlp;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export default abstract class EventWrapper<
|
||||
*
|
||||
* @param nlp - NLP parse results
|
||||
*/
|
||||
setNLP(nlp: Nlp.ParseEntities) {
|
||||
setNLP(nlp: NLU.ParseEntities) {
|
||||
this._nlp = nlp;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ import { SubscriberService } from '../services/subscriber.service';
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('message')
|
||||
export class MessageController extends BaseController<
|
||||
Message,
|
||||
AnyMessage,
|
||||
MessageStub,
|
||||
MessagePopulate,
|
||||
MessageFull
|
||||
|
||||
@@ -72,7 +72,7 @@ export class MessageRepository extends BaseRepository<
|
||||
until = new Date(),
|
||||
limit: number = 30,
|
||||
) {
|
||||
return await this.findPage(
|
||||
return await this.find(
|
||||
{
|
||||
$or: [{ recipient: subscriber.id }, { sender: subscriber.id }],
|
||||
createdAt: { $lt: until },
|
||||
@@ -96,7 +96,7 @@ export class MessageRepository extends BaseRepository<
|
||||
since = new Date(),
|
||||
limit: number = 30,
|
||||
) {
|
||||
return await this.findPage(
|
||||
return await this.find(
|
||||
{
|
||||
$or: [{ recipient: subscriber.id }, { sender: subscriber.id }],
|
||||
createdAt: { $gt: since },
|
||||
|
||||
@@ -106,7 +106,7 @@ export class SubscriberRepository extends BaseRepository<
|
||||
* @returns The constructed query object.
|
||||
*/
|
||||
findByForeignIdQuery(id: string) {
|
||||
return this.findPageQuery(
|
||||
return this.findQuery(
|
||||
{ foreign_id: id },
|
||||
{ skip: 0, limit: 1, sort: ['lastvisit', 'desc'] },
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { ChannelName } from '@/channel/types';
|
||||
import { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
|
||||
import { Subscriber } from '../subscriber.schema';
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface Context {
|
||||
channel?: ChannelName;
|
||||
text?: string;
|
||||
payload?: Payload | string;
|
||||
nlp?: Nlp.ParseEntities | null;
|
||||
nlp?: NLU.ParseEntities | null;
|
||||
vars: { [key: string]: any };
|
||||
user_location: {
|
||||
address?: Record<string, string>;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import EventWrapper from '@/channel/lib/EventWrapper';
|
||||
import { ContentService } from '@/cms/services/content.service';
|
||||
import { CONSOLE_CHANNEL_NAME } from '@/extensions/channels/console/settings';
|
||||
import { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
import { I18nService } from '@/i18n/services/i18n.service';
|
||||
import { LanguageService } from '@/i18n/services/language.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
@@ -254,7 +254,7 @@ export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
|
||||
* @returns The NLP patterns that matches
|
||||
*/
|
||||
matchNLP(
|
||||
nlp: Nlp.ParseEntities,
|
||||
nlp: NLU.ParseEntities,
|
||||
block: Block | BlockFull,
|
||||
): NlpPattern[] | undefined {
|
||||
// No nlp entities to check against
|
||||
|
||||
@@ -126,7 +126,7 @@ export class MessageService extends BaseService<
|
||||
* @returns The message history since the specified date.
|
||||
*/
|
||||
async findLastMessages(subscriber: Subscriber, limit: number = 5) {
|
||||
const lastMessages = await this.findPage(
|
||||
const lastMessages = await this.find(
|
||||
{
|
||||
$or: [{ sender: subscriber.id }, { recipient: subscriber.id }],
|
||||
},
|
||||
|
||||
@@ -283,10 +283,7 @@ export class ContentController extends BaseController<
|
||||
);
|
||||
throw new NotFoundException(`ContentType of id ${contentType} not found`);
|
||||
}
|
||||
return await this.contentService.findPage(
|
||||
{ entity: contentType },
|
||||
pageQuery,
|
||||
);
|
||||
return await this.contentService.find({ entity: contentType }, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -164,8 +164,8 @@ export class MenuController extends BaseController<
|
||||
@CsrfCheck(true)
|
||||
@Patch(':id')
|
||||
async updateOne(@Body() body: MenuCreateDto, @Param('id') id: string) {
|
||||
if (!id) return this.create(body);
|
||||
return this.menuService.updateOne(id, body);
|
||||
if (!id) return await this.create(body);
|
||||
return await this.menuService.updateOne(id, body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,7 +100,7 @@ export class ContentRepository extends BaseRepository<
|
||||
* @returns A promise that resolves to the matching content documents.
|
||||
*/
|
||||
async textSearch(query: string) {
|
||||
return this.find({
|
||||
return await this.find({
|
||||
$text: {
|
||||
$search: query,
|
||||
$diacriticSensitive: false,
|
||||
|
||||
@@ -49,7 +49,7 @@ export class ContentService extends BaseService<
|
||||
* @return A list of content matching the search query.
|
||||
*/
|
||||
async textSearch(query: string) {
|
||||
return this.repository.textSearch(query);
|
||||
return await this.repository.textSearch(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ export class ContentService extends BaseService<
|
||||
}
|
||||
|
||||
try {
|
||||
const contents = await this.findPage(query, {
|
||||
const contents = await this.find(query, {
|
||||
skip,
|
||||
limit,
|
||||
sort: ['createdAt', 'desc'],
|
||||
|
||||
@@ -6,7 +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 { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
|
||||
import { NlpParseResultType, RasaNlu } from '../types';
|
||||
|
||||
@@ -100,7 +100,7 @@ export const nlpParseResult: NlpParseResultType = {
|
||||
text: 'Hello Joe',
|
||||
};
|
||||
|
||||
export const nlpBestGuess: Nlp.ParseEntities = {
|
||||
export const nlpBestGuess: NLU.ParseEntities = {
|
||||
entities: [
|
||||
{
|
||||
start: 5,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { HelperService } from '@/helper/helper.service';
|
||||
import BaseNlpHelper from '@/helper/lib/base-nlp-helper';
|
||||
import { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
import { LanguageService } from '@/i18n/services/language.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { NlpEntity, NlpEntityFull } from '@/nlp/schemas/nlp-entity.schema';
|
||||
@@ -191,10 +191,10 @@ export default class CoreNluHelper extends BaseNlpHelper<
|
||||
async filterEntitiesByConfidence(
|
||||
nlp: NlpParseResultType,
|
||||
threshold: boolean,
|
||||
): Promise<Nlp.ParseEntities> {
|
||||
): Promise<NLU.ParseEntities> {
|
||||
try {
|
||||
let minConfidence = 0;
|
||||
const guess: Nlp.ParseEntities = {
|
||||
const guess: NLU.ParseEntities = {
|
||||
entities: nlp.entities.slice(),
|
||||
};
|
||||
if (threshold) {
|
||||
@@ -255,7 +255,7 @@ export default class CoreNluHelper extends BaseNlpHelper<
|
||||
text: string,
|
||||
threshold: boolean,
|
||||
project: string = 'current',
|
||||
): Promise<Nlp.ParseEntities> {
|
||||
): Promise<NLU.ParseEntities> {
|
||||
try {
|
||||
const settings = await this.getSettings();
|
||||
const { data: nlp } =
|
||||
@@ -272,7 +272,7 @@ export default class CoreNluHelper extends BaseNlpHelper<
|
||||
},
|
||||
);
|
||||
|
||||
return this.filterEntitiesByConfidence(nlp, threshold);
|
||||
return await this.filterEntitiesByConfidence(nlp, threshold);
|
||||
} catch (err) {
|
||||
this.logger.error('Core NLU Helper : Unable to parse nlp', err);
|
||||
throw err;
|
||||
|
||||
5
api/src/extensions/helpers/llm-nlu/i18n/en/help.json
Normal file
5
api/src/extensions/helpers/llm-nlu/i18n/en/help.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "Specify the name of the LLM (Large Language Model) you want to use. Leave this field empty if you prefer to use the default model specified in the LLM helper's settings.",
|
||||
"language_classifier_prompt_template": "Provide the prompt template used for language detection. Use Handlebars syntax to dynamically insert variables or customize the prompt based on your requirements.",
|
||||
"trait_classifier_prompt_template": "Define the prompt template for trait classification tasks, such as intent or sentiment detection. Use Handlebars syntax to structure and format the prompt appropriately."
|
||||
}
|
||||
5
api/src/extensions/helpers/llm-nlu/i18n/en/label.json
Normal file
5
api/src/extensions/helpers/llm-nlu/i18n/en/label.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "LLM Model",
|
||||
"language_classifier_prompt_template": "Language Detection Prompt Template",
|
||||
"trait_classifier_prompt_template": "Trait Classifier Prompt Template"
|
||||
}
|
||||
3
api/src/extensions/helpers/llm-nlu/i18n/en/title.json
Normal file
3
api/src/extensions/helpers/llm-nlu/i18n/en/title.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"llm_nlu_helper": "LLM NLU Engine"
|
||||
}
|
||||
5
api/src/extensions/helpers/llm-nlu/i18n/fr/help.json
Normal file
5
api/src/extensions/helpers/llm-nlu/i18n/fr/help.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "Spécifiez le nom du modèle LLM que vous souhaitez utiliser. Laissez ce champ vide si vous préférez utiliser le modèle par défaut spécifié dans les paramètres de l'assistant LLM.",
|
||||
"language_classifier_prompt_template": "Fournissez le modèle de prompt utilisé pour la détection de langue. Utilisez la syntaxe Handlebars pour insérer dynamiquement des variables ou personnaliser le prompt en fonction de vos besoins.",
|
||||
"trait_classifier_prompt_template": "Définissez le modèle de prompt pour les tâches de classification des traits, telles que la détection d'intention ou de sentiment. Utilisez la syntaxe Handlebars pour structurer et formater correctement le prompt."
|
||||
}
|
||||
5
api/src/extensions/helpers/llm-nlu/i18n/fr/label.json
Normal file
5
api/src/extensions/helpers/llm-nlu/i18n/fr/label.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "Modèle LLM",
|
||||
"language_classifier_prompt_template": "Modèle de prompt de détection de langue",
|
||||
"trait_classifier_prompt_template": "Modèle de prompt du classificateur de traits"
|
||||
}
|
||||
3
api/src/extensions/helpers/llm-nlu/i18n/fr/title.json
Normal file
3
api/src/extensions/helpers/llm-nlu/i18n/fr/title.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"llm_nlu_helper": "Moteur LLM NLU"
|
||||
}
|
||||
22
api/src/extensions/helpers/llm-nlu/index.d.ts
vendored
Normal file
22
api/src/extensions/helpers/llm-nlu/index.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2024 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.
|
||||
* 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 LLM_NLU_HELPER_SETTINGS, { LLM_NLU_HELPER_NAMESPACE } from './settings';
|
||||
|
||||
declare global {
|
||||
interface Settings extends SettingTree<typeof LLM_NLU_HELPER_SETTINGS> {}
|
||||
}
|
||||
|
||||
declare module '@nestjs/event-emitter' {
|
||||
interface IHookExtensionsOperationMap {
|
||||
[LLM_NLU_HELPER_NAMESPACE]: TDefinition<
|
||||
object,
|
||||
SettingMapByType<typeof LLM_NLU_HELPER_SETTINGS>
|
||||
>;
|
||||
}
|
||||
}
|
||||
186
api/src/extensions/helpers/llm-nlu/index.helper.ts
Normal file
186
api/src/extensions/helpers/llm-nlu/index.helper.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright © 2024 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.
|
||||
* 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 { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { HelperService } from '@/helper/helper.service';
|
||||
import BaseNlpHelper from '@/helper/lib/base-nlp-helper';
|
||||
import { LLM, NLU } from '@/helper/types';
|
||||
import { LanguageService } from '@/i18n/services/language.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { NlpEntityFull } from '@/nlp/schemas/nlp-entity.schema';
|
||||
import { NlpEntityService } from '@/nlp/services/nlp-entity.service';
|
||||
import { SettingService } from '@/setting/services/setting.service';
|
||||
|
||||
import { LLM_NLU_HELPER_NAME } from './settings';
|
||||
|
||||
@Injectable()
|
||||
export default class LlmNluHelper
|
||||
extends BaseNlpHelper<typeof LLM_NLU_HELPER_NAME>
|
||||
implements OnModuleInit
|
||||
{
|
||||
private languageClassifierPrompt: string;
|
||||
|
||||
/**
|
||||
* Trait prompts dictionary by id
|
||||
*/
|
||||
private traitClassifierPrompts: Array<NlpEntityFull & { prompt: string }>;
|
||||
|
||||
constructor(
|
||||
settingService: SettingService,
|
||||
helperService: HelperService,
|
||||
logger: LoggerService,
|
||||
private readonly languageService: LanguageService,
|
||||
private readonly nlpEntityService: NlpEntityService,
|
||||
) {
|
||||
super(LLM_NLU_HELPER_NAME, settingService, helperService, logger);
|
||||
}
|
||||
|
||||
getPath() {
|
||||
return __dirname;
|
||||
}
|
||||
|
||||
@OnEvent('hook:language:*')
|
||||
@OnEvent('hook:llm_nlu_helper:language_classifier_prompt_template')
|
||||
async buildLanguageClassifierPrompt() {
|
||||
const settings = await this.getSettings();
|
||||
if (settings) {
|
||||
const languages = await this.languageService.findAll();
|
||||
const delegate = Handlebars.compile(
|
||||
settings.language_classifier_prompt_template,
|
||||
);
|
||||
this.languageClassifierPrompt = delegate({ languages });
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent('hook:nlpEntity:*')
|
||||
@OnEvent('hook:nlpValue:*')
|
||||
@OnEvent('hook:llm_nlu_helper:trait_classifier_prompt_template')
|
||||
async buildClassifiersPrompt() {
|
||||
const settings = await this.getSettings();
|
||||
if (settings) {
|
||||
const entities = await this.nlpEntityService.findAndPopulate({
|
||||
lookups: 'trait',
|
||||
});
|
||||
const traitEntities = entities.filter(({ lookups }) =>
|
||||
lookups.includes('trait'),
|
||||
);
|
||||
this.traitClassifierPrompts = traitEntities.map((entity) => ({
|
||||
...entity,
|
||||
prompt: Handlebars.compile(settings.trait_classifier_prompt_template)({
|
||||
entity,
|
||||
}),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
super.onModuleInit();
|
||||
|
||||
await this.buildLanguageClassifierPrompt();
|
||||
await this.buildClassifiersPrompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities in a given text based on their values and synonyms.
|
||||
*
|
||||
* This function takes a string of text and an array of entities, where each entity contains a value
|
||||
* and a list of synonyms. It returns an array of objects, each representing an entity found in the text
|
||||
* along with its start and end positions.
|
||||
*
|
||||
* @param text - The input text to search for entities.
|
||||
* @param entities - An array of entities to search for, each containing a `value` and a list of `synonyms`.
|
||||
*
|
||||
* @returns An array of objects representing the found entities, with their `value`, `start`, and `end` positions.
|
||||
*/
|
||||
private findKeywordEntities(
|
||||
text: string,
|
||||
entity: NlpEntityFull,
|
||||
): NLU.ParseEntity[] {
|
||||
return entity.values
|
||||
.flatMap(({ value, expressions }) => {
|
||||
const allValues = [value, ...expressions];
|
||||
|
||||
// Filter the terms that are found in the text
|
||||
return allValues
|
||||
.flatMap((term) => {
|
||||
const regex = new RegExp(`\\b${term}\\b`, 'g');
|
||||
const matches = [...text.matchAll(regex)];
|
||||
|
||||
// Map matches to FoundEntity format
|
||||
return matches.map((match) => ({
|
||||
entity: entity.name,
|
||||
value: term,
|
||||
start: match.index!,
|
||||
end: match.index! + term.length,
|
||||
confidence: 1,
|
||||
}));
|
||||
})
|
||||
.shift();
|
||||
})
|
||||
.filter((v) => !!v);
|
||||
}
|
||||
|
||||
async predict(text: string): Promise<NLU.ParseEntities> {
|
||||
const settings = await this.getSettings();
|
||||
const helper = await this.helperService.getDefaultLlmHelper();
|
||||
const defaultLanguage = await this.languageService.getDefaultLanguage();
|
||||
// Detect language
|
||||
const language = await helper.generateStructuredResponse<string>(
|
||||
`input text: ${text}`,
|
||||
settings.model,
|
||||
this.languageClassifierPrompt,
|
||||
{
|
||||
type: LLM.ResponseSchemaType.STRING,
|
||||
description: 'Language of the input text',
|
||||
},
|
||||
);
|
||||
|
||||
const traits: NLU.ParseEntity[] = [
|
||||
{
|
||||
entity: 'language',
|
||||
value: language || defaultLanguage.code,
|
||||
confidence: undefined,
|
||||
},
|
||||
];
|
||||
for await (const { name, doc, prompt, values } of this
|
||||
.traitClassifierPrompts) {
|
||||
const allowedValues = values.map(({ value }) => value);
|
||||
const result = await helper.generateStructuredResponse<string>(
|
||||
`input text: ${text}`,
|
||||
settings.model,
|
||||
prompt,
|
||||
{
|
||||
type: LLM.ResponseSchemaType.STRING,
|
||||
description: `${name}${doc ? `: ${doc}` : ''}`,
|
||||
enum: allowedValues.concat('unknown'),
|
||||
},
|
||||
);
|
||||
const safeValue = result.toLowerCase().trim();
|
||||
const value = allowedValues.includes(safeValue) ? safeValue : '';
|
||||
traits.push({
|
||||
entity: name,
|
||||
value,
|
||||
confidence: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// Perform slot filling in a deterministic way since
|
||||
// it's currently a challenging task for the LLMs.
|
||||
const keywordEntities = await this.nlpEntityService.findAndPopulate({
|
||||
lookups: 'keywords',
|
||||
});
|
||||
const entities = keywordEntities.flatMap((keywordEntity) =>
|
||||
this.findKeywordEntities(text, keywordEntity),
|
||||
);
|
||||
|
||||
return { entities: traits.concat(entities) };
|
||||
}
|
||||
}
|
||||
8
api/src/extensions/helpers/llm-nlu/package.json
Normal file
8
api/src/extensions/helpers/llm-nlu/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "hexabot-helper-llm-nlu",
|
||||
"version": "2.0.0",
|
||||
"description": "The LLM NLU Helper Extension for Hexabot to enable the Intent Classification and Language Detection",
|
||||
"dependencies": {},
|
||||
"author": "Hexastack",
|
||||
"license": "AGPL-3.0-only"
|
||||
}
|
||||
47
api/src/extensions/helpers/llm-nlu/settings.ts
Normal file
47
api/src/extensions/helpers/llm-nlu/settings.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2024 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.
|
||||
* 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 { HelperSetting } from '@/helper/types';
|
||||
import { SettingType } from '@/setting/schemas/types';
|
||||
|
||||
export const LLM_NLU_HELPER_NAME = 'llm-nlu-helper';
|
||||
|
||||
export const LLM_NLU_HELPER_NAMESPACE = 'llm_nlu_helper';
|
||||
|
||||
export default [
|
||||
{
|
||||
group: LLM_NLU_HELPER_NAMESPACE,
|
||||
label: 'model',
|
||||
value: '',
|
||||
type: SettingType.text,
|
||||
},
|
||||
{
|
||||
group: LLM_NLU_HELPER_NAMESPACE,
|
||||
label: 'language_classifier_prompt_template',
|
||||
value: `You are an advanced language detection assistant. Your task is to identify the language of the given input text from the following supported languages:
|
||||
|
||||
{{#each languages}}
|
||||
- {{title}} (code={{code}})
|
||||
{{/each}}
|
||||
|
||||
Provide a concise result by stating the language code only. If the language is not in the supported list, return an empty string.`,
|
||||
type: SettingType.textarea,
|
||||
},
|
||||
{
|
||||
group: LLM_NLU_HELPER_NAMESPACE,
|
||||
label: 'trait_classifier_prompt_template',
|
||||
value: `You are an advanced text classification assistant. Your task is to classify the given input text provided in the following {{entity.name}} values:
|
||||
|
||||
{{#each entity.values}}
|
||||
- {{value}}
|
||||
{{/each}}
|
||||
|
||||
Provide a concise result by stating only the value of the {{entity.name}}. Return an empty string otherwise.`,
|
||||
type: SettingType.textarea,
|
||||
},
|
||||
] as const satisfies HelperSetting<typeof LLM_NLU_HELPER_NAME>[];
|
||||
@@ -10,6 +10,8 @@ import { HttpModule } from '@nestjs/axios';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { InjectDynamicProviders } from 'nestjs-dynamic-providers';
|
||||
|
||||
import { NlpModule } from '@/nlp/nlp.module';
|
||||
|
||||
import { HelperController } from './helper.controller';
|
||||
import { HelperService } from './helper.service';
|
||||
|
||||
@@ -23,7 +25,7 @@ import { HelperService } from './helper.service';
|
||||
'dist/.hexabot/custom/extensions/helpers/**/*.helper.js',
|
||||
)
|
||||
@Module({
|
||||
imports: [HttpModule],
|
||||
imports: [HttpModule, NlpModule],
|
||||
controllers: [HelperController],
|
||||
providers: [HelperService],
|
||||
exports: [HelperService],
|
||||
|
||||
@@ -11,7 +11,7 @@ import { LoggerService } from '@/logger/logger.service';
|
||||
import { SettingService } from '@/setting/services/setting.service';
|
||||
|
||||
import { HelperService } from '../helper.service';
|
||||
import { HelperName, HelperType } from '../types';
|
||||
import { HelperName, HelperType, LLM } from '../types';
|
||||
|
||||
import BaseHelper from './base-helper';
|
||||
|
||||
@@ -30,7 +30,7 @@ export default abstract class BaseLlmHelper<
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a response using LLM
|
||||
* Generates a text response using LLM
|
||||
*
|
||||
* @param prompt - The input text from the user
|
||||
* @param model - The model to be used
|
||||
@@ -45,6 +45,24 @@ export default abstract class BaseLlmHelper<
|
||||
extra?: any,
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
* Generates a structured response using LLM
|
||||
*
|
||||
* @param prompt - The input text from the user
|
||||
* @param model - The model to be used
|
||||
* @param systemPrompt - The input text from the system
|
||||
* @param schema - The OpenAPI data schema
|
||||
* @param extra - Extra options
|
||||
* @returns {Promise<string>} - The generated response from the LLM
|
||||
*/
|
||||
generateStructuredResponse?<T>(
|
||||
prompt: string,
|
||||
model: string,
|
||||
systemPrompt: string,
|
||||
schema: LLM.ResponseSchema,
|
||||
extra?: any,
|
||||
): Promise<T>;
|
||||
|
||||
/**
|
||||
* Send a chat completion request with the conversation history.
|
||||
* You can use this same approach to start the conversation
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import { SettingService } from '@/setting/services/setting.service';
|
||||
|
||||
import { HelperService } from '../helper.service';
|
||||
import { HelperName, HelperType, Nlp } from '../types';
|
||||
import { HelperName, HelperType, NLU } from '../types';
|
||||
|
||||
import BaseHelper from './base-helper';
|
||||
|
||||
@@ -119,7 +119,7 @@ export default abstract class BaseNlpHelper<
|
||||
*
|
||||
* @returns The formatted NLP training set
|
||||
*/
|
||||
abstract format(samples: NlpSampleFull[], entities: NlpEntityFull[]): unknown;
|
||||
format?(samples: NlpSampleFull[], entities: NlpEntityFull[]): unknown;
|
||||
|
||||
/**
|
||||
* Perform training request
|
||||
@@ -129,10 +129,7 @@ export default abstract class BaseNlpHelper<
|
||||
*
|
||||
* @returns Training result
|
||||
*/
|
||||
abstract train(
|
||||
samples: NlpSampleFull[],
|
||||
entities: NlpEntityFull[],
|
||||
): Promise<any>;
|
||||
train?(samples: NlpSampleFull[], entities: NlpEntityFull[]): Promise<any>;
|
||||
|
||||
/**
|
||||
* Perform evaluation request
|
||||
@@ -142,10 +139,7 @@ export default abstract class BaseNlpHelper<
|
||||
*
|
||||
* @returns NLP evaluation result
|
||||
*/
|
||||
abstract evaluate(
|
||||
samples: NlpSampleFull[],
|
||||
entities: NlpEntityFull[],
|
||||
): Promise<any>;
|
||||
evaluate?(samples: NlpSampleFull[], entities: NlpEntityFull[]): Promise<any>;
|
||||
|
||||
/**
|
||||
* Delete/Forget a sample
|
||||
@@ -154,7 +148,7 @@ export default abstract class BaseNlpHelper<
|
||||
*
|
||||
* @returns The deleted sample otherwise an error
|
||||
*/
|
||||
async forget(sample: NlpSample): Promise<NlpSample> {
|
||||
async forget?(sample: NlpSample): Promise<NlpSample> {
|
||||
return sample;
|
||||
}
|
||||
|
||||
@@ -166,10 +160,10 @@ export default abstract class BaseNlpHelper<
|
||||
*
|
||||
* @returns NLP Parsed entities
|
||||
*/
|
||||
abstract filterEntitiesByConfidence(
|
||||
filterEntitiesByConfidence?(
|
||||
nlp: any,
|
||||
threshold: boolean,
|
||||
): Promise<Nlp.ParseEntities>;
|
||||
): Promise<NLU.ParseEntities>;
|
||||
|
||||
/**
|
||||
* Returns only the entities that have strong confidence (> than the threshold), can return an empty result
|
||||
@@ -184,5 +178,5 @@ export default abstract class BaseNlpHelper<
|
||||
text: string,
|
||||
threshold?: boolean,
|
||||
project?: string,
|
||||
): Promise<Nlp.ParseEntities>;
|
||||
): Promise<NLU.ParseEntities>;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,7 @@ import BaseHelper from './lib/base-helper';
|
||||
import BaseLlmHelper from './lib/base-llm-helper';
|
||||
import BaseNlpHelper from './lib/base-nlp-helper';
|
||||
|
||||
export namespace Nlp {
|
||||
export interface Config {
|
||||
endpoint?: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export namespace NLU {
|
||||
export interface ParseEntity {
|
||||
entity: string; // Entity name
|
||||
value: string; // Value name
|
||||
@@ -32,6 +27,60 @@ export namespace Nlp {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace LLM {
|
||||
/**
|
||||
* Schema is used to define the format of input/output data.
|
||||
* Represents a select subset of an OpenAPI 3.0 schema object.
|
||||
* More fields may be added in the future as needed.
|
||||
* @public
|
||||
*/
|
||||
export interface ResponseSchema {
|
||||
/**
|
||||
* Optional. The type of the property. {@link
|
||||
* SchemaType}.
|
||||
*/
|
||||
type?: ResponseSchemaType;
|
||||
/** Optional. The format of the property. */
|
||||
format?: string;
|
||||
/** Optional. The description of the property. */
|
||||
description?: string;
|
||||
/** Optional. Whether the property is nullable. */
|
||||
nullable?: boolean;
|
||||
/** Optional. The items of the property. */
|
||||
items?: ResponseSchema;
|
||||
/** Optional. The enum of the property. */
|
||||
enum?: string[];
|
||||
/** Optional. Map of {@link Schema}. */
|
||||
properties?: {
|
||||
[k: string]: ResponseSchema;
|
||||
};
|
||||
/** Optional. Array of required property. */
|
||||
required?: string[];
|
||||
/** Optional. The example of the property. */
|
||||
example?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the list of OpenAPI data types
|
||||
* as defined by https://swagger.io/docs/specification/data-models/data-types/
|
||||
* @public
|
||||
*/
|
||||
export enum ResponseSchemaType {
|
||||
/** String type. */
|
||||
STRING = 'string',
|
||||
/** Number type. */
|
||||
NUMBER = 'number',
|
||||
/** Integer type. */
|
||||
INTEGER = 'integer',
|
||||
/** Boolean type. */
|
||||
BOOLEAN = 'boolean',
|
||||
/** Array type. */
|
||||
ARRAY = 'array',
|
||||
/** Object type. */
|
||||
OBJECT = 'object',
|
||||
}
|
||||
}
|
||||
|
||||
export enum HelperType {
|
||||
NLU = 'nlu',
|
||||
LLM = 'llm',
|
||||
|
||||
@@ -137,7 +137,7 @@ export class TranslationController extends BaseController<Translation> {
|
||||
);
|
||||
await Promise.all(queue);
|
||||
// Purge non existing translations
|
||||
return this.translationService.deleteMany({
|
||||
return await this.translationService.deleteMany({
|
||||
str: { $nin: strings },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,6 +74,6 @@ export class NlpSampleEntityService extends BaseService<
|
||||
} as NlpSampleEntity;
|
||||
});
|
||||
|
||||
return this.createMany(sampleEntities);
|
||||
return await this.createMany(sampleEntities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export const DEFAULT_SETTINGS = [
|
||||
{
|
||||
group: 'chatbot_settings',
|
||||
label: 'default_nlu_helper',
|
||||
value: 'core-nlu-helper',
|
||||
value: 'llm-nlu-helper',
|
||||
type: SettingType.select,
|
||||
config: {
|
||||
multiple: false,
|
||||
|
||||
@@ -84,7 +84,7 @@ export class ReadOnlyUserController extends BaseController<
|
||||
@Roles('public')
|
||||
@Get('bot/profile_pic')
|
||||
async botProfilePic(@Query('color') color: string) {
|
||||
return getBotAvatar(color);
|
||||
return await getBotAvatar(color);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ export class ReadOnlyUserController extends BaseController<
|
||||
} catch (e) {
|
||||
const user = await this.userService.findOne(id);
|
||||
if (user) {
|
||||
return generateInitialsAvatar(user);
|
||||
return await generateInitialsAvatar(user);
|
||||
} else {
|
||||
throw new NotFoundException(`user with ID ${id} not found`);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ export class PasswordResetService {
|
||||
* @returns The signed JWT token.
|
||||
*/
|
||||
async sign(dto: UserRequestResetDto) {
|
||||
return this.jwtService.signAsync(dto, this.jwtSignOptions);
|
||||
return await this.jwtService.signAsync(dto, this.jwtSignOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +138,6 @@ export class PasswordResetService {
|
||||
* @returns The decoded payload of the token.
|
||||
*/
|
||||
async verify(token: string): Promise<UserRequestResetDto> {
|
||||
return this.jwtService.verifyAsync(token, this.jwtSignOptions);
|
||||
return await this.jwtService.verifyAsync(token, this.jwtSignOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class ValidateAccountService {
|
||||
* @returns A promise that resolves to the signed JWT token.
|
||||
*/
|
||||
async sign(dto: { email: string }) {
|
||||
return this.jwtService.signAsync(dto, this.jwtSignOptions);
|
||||
return await this.jwtService.signAsync(dto, this.jwtSignOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +61,7 @@ export class ValidateAccountService {
|
||||
* @returns A promise that resolves to an object containing the user's email.
|
||||
*/
|
||||
async verify(token: string): Promise<{ email: string }> {
|
||||
return this.jwtService.verifyAsync(token, this.jwtSignOptions);
|
||||
return await this.jwtService.verifyAsync(token, this.jwtSignOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
SortOrder,
|
||||
UpdateQuery,
|
||||
UpdateWithAggregationPipeline,
|
||||
UpdateWriteOpResult,
|
||||
} from 'mongoose';
|
||||
|
||||
import { TFilterQuery } from '@/utils/types/filter.types';
|
||||
@@ -70,7 +71,7 @@ export abstract class BaseRepository<
|
||||
this.registerLifeCycleHooks();
|
||||
}
|
||||
|
||||
getPopulate() {
|
||||
getPopulate(): P[] {
|
||||
return this.populate;
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ export abstract class BaseRepository<
|
||||
return `hook:${entity}:${suffix}` as `hook:${IHookEntities}:${TNormalizedEvents}`;
|
||||
}
|
||||
|
||||
private registerLifeCycleHooks() {
|
||||
private registerLifeCycleHooks(): void {
|
||||
const repository = this;
|
||||
const hooks = LifecycleHookManager.getHooks(this.cls.name);
|
||||
|
||||
@@ -202,7 +203,7 @@ export abstract class BaseRepository<
|
||||
protected async execute<R extends Omit<T, P>>(
|
||||
query: Query<T[], T>,
|
||||
cls: new () => R,
|
||||
) {
|
||||
): Promise<R[]> {
|
||||
const resultSet = await query.lean(this.leanOpts).exec();
|
||||
return resultSet.map((doc) => plainToClass(cls, doc, this.transformOpts));
|
||||
}
|
||||
@@ -211,7 +212,7 @@ export abstract class BaseRepository<
|
||||
query: Query<T, T>,
|
||||
cls: new () => R,
|
||||
options?: ClassTransformOptions,
|
||||
) {
|
||||
): Promise<R> {
|
||||
const doc = await query.lean(this.leanOpts).exec();
|
||||
return plainToClass(cls, doc, options ?? this.transformOpts);
|
||||
}
|
||||
@@ -219,7 +220,7 @@ export abstract class BaseRepository<
|
||||
protected findOneQuery(
|
||||
criteria: string | TFilterQuery<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
): Query<T, T, object, T, 'findOne', object> {
|
||||
if (!criteria) {
|
||||
// An empty criteria would return the first document that it finds
|
||||
throw new Error('findOneQuery() should not have an empty criteria');
|
||||
@@ -247,7 +248,7 @@ export abstract class BaseRepository<
|
||||
async findOneAndPopulate(
|
||||
criteria: string | TFilterQuery<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
): Promise<TFull> {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findOneQuery(criteria, projection).populate(
|
||||
this.populate,
|
||||
@@ -259,8 +260,32 @@ export abstract class BaseRepository<
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
const { skip = 0, limit, sort = ['createdAt', 'asc'] } = pageQuery || {};
|
||||
): Query<T[], T, object, T, 'find', object>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
protected findQuery(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Query<T[], T, object, T, 'find', object>;
|
||||
|
||||
protected findQuery(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Query<T[], T, object, T, 'find', object> {
|
||||
if (Array.isArray(pageQuery)) {
|
||||
const query = this.model.find<T>(filter, projection);
|
||||
return query.sort([pageQuery] as [string, SortOrder][]);
|
||||
}
|
||||
|
||||
const {
|
||||
skip = 0,
|
||||
limit = 0,
|
||||
sort = ['createdAt', 'asc'],
|
||||
} = pageQuery || {};
|
||||
const query = this.model.find<T>(filter, projection);
|
||||
return query
|
||||
.skip(skip)
|
||||
@@ -272,12 +297,32 @@ export abstract class BaseRepository<
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async find(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<T[]>;
|
||||
|
||||
async find(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<T[]> {
|
||||
if (Array.isArray(pageQuery)) {
|
||||
const query = this.findQuery(filter, pageQuery, projection);
|
||||
return await this.execute(query, this.cls);
|
||||
}
|
||||
|
||||
const query = this.findQuery(filter, pageQuery, projection);
|
||||
return await this.execute(query, this.cls);
|
||||
}
|
||||
|
||||
private ensureCanPopulate() {
|
||||
private ensureCanPopulate(): void {
|
||||
if (!this.populate || !this.clsPopulate) {
|
||||
throw new Error('Cannot populate query');
|
||||
}
|
||||
@@ -287,23 +332,47 @@ export abstract class BaseRepository<
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
): Promise<TFull[]>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async findAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<TFull[]>;
|
||||
|
||||
async findAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<TFull[]> {
|
||||
this.ensureCanPopulate();
|
||||
if (Array.isArray(pageQuery)) {
|
||||
const query = this.findQuery(filters, pageQuery, projection).populate(
|
||||
this.populate,
|
||||
);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
const query = this.findQuery(filters, pageQuery, projection).populate(
|
||||
this.populate,
|
||||
);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
protected findAllQuery(sort?: QuerySortDto<T>) {
|
||||
return this.findQuery({}, { limit: undefined, skip: undefined, sort });
|
||||
protected findAllQuery(
|
||||
sort?: QuerySortDto<T>,
|
||||
): Query<T[], T, object, T, 'find', object> {
|
||||
return this.findQuery({}, { limit: 0, skip: 0, sort });
|
||||
}
|
||||
|
||||
async findAll(sort?: QuerySortDto<T>) {
|
||||
return await this.find({}, { limit: undefined, skip: undefined, sort });
|
||||
async findAll(sort?: QuerySortDto<T>): Promise<T[]> {
|
||||
return await this.find({}, { limit: 0, skip: 0, sort });
|
||||
}
|
||||
|
||||
async findAllAndPopulate(sort?: QuerySortDto<T>) {
|
||||
async findAllAndPopulate(sort?: QuerySortDto<T>): Promise<TFull[]> {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findAllQuery(sort).populate(this.populate);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
@@ -315,7 +384,7 @@ export abstract class BaseRepository<
|
||||
protected findPageQuery(
|
||||
filters: TFilterQuery<T>,
|
||||
{ skip, limit, sort }: PageQueryDto<T>,
|
||||
) {
|
||||
): Query<T[], T, object, T, 'find', object> {
|
||||
return this.findQuery(filters)
|
||||
.skip(skip)
|
||||
.limit(limit)
|
||||
@@ -339,7 +408,7 @@ export abstract class BaseRepository<
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery: PageQueryDto<T>,
|
||||
) {
|
||||
): Promise<TFull[]> {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findPageQuery(filters, pageQuery).populate(
|
||||
this.populate,
|
||||
@@ -365,7 +434,7 @@ export abstract class BaseRepository<
|
||||
);
|
||||
}
|
||||
|
||||
async createMany(dtoArray: U[]) {
|
||||
async createMany(dtoArray: U[]): Promise<T[]> {
|
||||
const docs = await this.model.create(dtoArray);
|
||||
|
||||
return docs.map((doc) =>
|
||||
@@ -394,7 +463,7 @@ export abstract class BaseRepository<
|
||||
async updateMany<D extends Partial<U>>(
|
||||
filter: TFilterQuery<T>,
|
||||
dto: UpdateQuery<D>,
|
||||
) {
|
||||
): Promise<UpdateWriteOpResult> {
|
||||
return await this.model.updateMany<T>(filter, {
|
||||
$set: dto,
|
||||
});
|
||||
@@ -410,19 +479,19 @@ export abstract class BaseRepository<
|
||||
return await this.model.deleteMany(criteria);
|
||||
}
|
||||
|
||||
async preValidate(_doc: HydratedDocument<T>) {
|
||||
async preValidate(_doc: HydratedDocument<T>): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async postValidate(_validated: HydratedDocument<T>) {
|
||||
async postValidate(_validated: HydratedDocument<T>): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async preCreate(_doc: HydratedDocument<T>) {
|
||||
async preCreate(_doc: HydratedDocument<T>): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async postCreate(_created: HydratedDocument<T>) {
|
||||
async postCreate(_created: HydratedDocument<T>): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
@@ -430,7 +499,7 @@ export abstract class BaseRepository<
|
||||
_query: Query<D, D, unknown, T, 'findOneAndUpdate'>,
|
||||
_criteria: TFilterQuery<T>,
|
||||
_updates: UpdateWithAggregationPipeline | UpdateQuery<D>,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
@@ -438,35 +507,35 @@ export abstract class BaseRepository<
|
||||
_query: Query<D, D, unknown, T, 'updateMany'>,
|
||||
_criteria: TFilterQuery<T>,
|
||||
_updates: UpdateWithAggregationPipeline | UpdateQuery<D>,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async postUpdateMany(
|
||||
_query: Query<D, D, unknown, T, 'updateMany'>,
|
||||
_updated: any,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async postUpdate(
|
||||
_query: Query<D, D, unknown, T, 'findOneAndUpdate'>,
|
||||
_updated: T,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async preDelete(
|
||||
_query: Query<DeleteResult, D, unknown, T, 'deleteOne' | 'deleteMany'>,
|
||||
_criteria: TFilterQuery<T>,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
|
||||
async postDelete(
|
||||
_query: Query<DeleteResult, D, unknown, T, 'deleteOne' | 'deleteMany'>,
|
||||
_result: DeleteResult,
|
||||
) {
|
||||
): Promise<void> {
|
||||
// Nothing ...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,25 @@ export abstract class BaseService<
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async find(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<T[]>;
|
||||
|
||||
async find(
|
||||
filter: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<T[]> {
|
||||
if (Array.isArray(pageQuery))
|
||||
return await this.repository.find(filter, pageQuery, projection);
|
||||
|
||||
return await this.repository.find(filter, pageQuery, projection);
|
||||
}
|
||||
|
||||
@@ -56,7 +74,29 @@ export abstract class BaseService<
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
) {
|
||||
): Promise<TFull[]>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async findAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<TFull[]>;
|
||||
|
||||
async findAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
|
||||
projection?: ProjectionType<T>,
|
||||
): Promise<TFull[]> {
|
||||
if (Array.isArray(pageQuery))
|
||||
return await this.repository.findAndPopulate(
|
||||
filters,
|
||||
pageQuery,
|
||||
projection,
|
||||
);
|
||||
|
||||
return await this.repository.findAndPopulate(
|
||||
filters,
|
||||
pageQuery,
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
* 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 { Nlp } from '@/helper/types';
|
||||
import { NLU } from '@/helper/types';
|
||||
|
||||
export const nlpEntitiesGreeting: Nlp.ParseEntities = {
|
||||
export const nlpEntitiesGreeting: NLU.ParseEntities = {
|
||||
entities: [
|
||||
{
|
||||
entity: 'intent',
|
||||
|
||||
@@ -149,7 +149,7 @@ export class SocketIoClient {
|
||||
url: string,
|
||||
options?: Partial<Omit<IOOutgoingMessage, "url" | "method" | "data">>,
|
||||
): Promise<IOIncomingMessage<T>> {
|
||||
return this.request({
|
||||
return await this.request({
|
||||
method: "get",
|
||||
url,
|
||||
...options,
|
||||
|
||||
@@ -167,7 +167,7 @@ export class SocketIoClient {
|
||||
url: string,
|
||||
options?: Partial<Omit<IOOutgoingMessage, "url" | "method" | "body">>,
|
||||
): Promise<IOIncomingMessage<T>> {
|
||||
return this.request({
|
||||
return await this.request({
|
||||
method: "get",
|
||||
url,
|
||||
...options,
|
||||
@@ -178,7 +178,7 @@ export class SocketIoClient {
|
||||
url: string,
|
||||
options: Partial<Omit<IOOutgoingMessage, "url" | "method">>,
|
||||
): Promise<IOIncomingMessage<T>> {
|
||||
return this.request({
|
||||
return await this.request({
|
||||
method: "post",
|
||||
url,
|
||||
...options,
|
||||
|
||||
Reference in New Issue
Block a user