new(basic.gblib): GPT replacing ALLEN NLP Reading Comp.

This commit is contained in:
Rodrigo Rodriguez 2024-02-05 12:36:20 -03:00
parent 218bcd1b3e
commit 681d20c5fe
10 changed files with 138 additions and 103 deletions

View file

@ -327,7 +327,7 @@ export class AdminDialog extends IGBDialog {
let sec = new SecService(); let sec = new SecService();
const member = step.context.activity.from; const member = step.context.activity.from;
const user = await sec.ensureUser( const user = await sec.ensureUser(
min.instance.instanceId, min,
member.id, member.id,
member.name, member.name,
'', '',

View file

@ -1220,7 +1220,7 @@ export class DialogKeywords {
let user = await sec.getUserFromSystemId(fromOrDialogName); let user = await sec.getUserFromSystemId(fromOrDialogName);
if (!user) { if (!user) {
user = await sec.ensureUser( user = await sec.ensureUser(
min.instance.instanceId, min,
fromOrDialogName, fromOrDialogName,
fromOrDialogName, fromOrDialogName,
null, null,

View file

@ -328,6 +328,10 @@ export class GBMinService {
if (!Fs.existsSync(dir)) { if (!Fs.existsSync(dir)) {
mkdirp.sync(dir); mkdirp.sync(dir);
} }
dir = `work/${gbai}/users`;
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
}
// Loads Named Entity data for this bot. // Loads Named Entity data for this bot.
@ -956,7 +960,7 @@ export class GBMinService {
const member = context.activity.from; const member = context.activity.from;
const sec = new SecService(); const sec = new SecService();
const user = await sec.ensureUser(instance.instanceId, member.id, member.name, '', 'web', member.name, null); const user = await sec.ensureUser(min, member.id, member.name, '', 'web', member.name, null);
const userId = user.userId; const userId = user.userId;
const params = user.params ? JSON.parse(user.params) : {}; const params = user.params ? JSON.parse(user.params) : {};
@ -1351,7 +1355,7 @@ export class GBMinService {
memberId = member.id; memberId = member.id;
} }
let user = await sec.ensureUser(min.instance.instanceId, memberId, member.name, '', 'web', member.name, email); let user = await sec.ensureUser(min, memberId, member.name, '', 'web', member.name, email);
const userId = user.userId; const userId = user.userId;
const params = user.params ? JSON.parse(user.params) : {}; const params = user.params ? JSON.parse(user.params) : {};

View file

@ -88,7 +88,7 @@ export class FeedbackDialog extends IGBDialog {
if (args && args.to) { if (args && args.to) {
// An user from Teams willing to transfer to a WhatsApp user. // An user from Teams willing to transfer to a WhatsApp user.
await sec.ensureUser(min.instance.instanceId, args.to, 'Name', '', 'whatsapp', 'Name', null); await sec.ensureUser(min, args.to, 'Name', '', 'whatsapp', 'Name', null);
await sec.assignHumanAgent(min, args.to, profile.userSystemId); await sec.assignHumanAgent(min, args.to, profile.userSystemId);
await min.conversationalService.sendText( await min.conversationalService.sendText(

View file

@ -137,7 +137,7 @@ export class GoogleChatDirectLine extends GBService {
message.ack(); message.ack();
const sec = new SecService(); const sec = new SecService();
const user = await sec.ensureUser(this.min.instance.instanceId, from, from, '', 'googlechat', fromName, from); const user = await sec.ensureUser(this.min, from, from, '', 'googlechat', fromName, from);
await sec.updateConversationReferenceById(user.userId, threadName); await sec.updateConversationReferenceById(user.userId, threadName);

View file

@ -32,14 +32,35 @@
import { GBMinInstance } from 'botlib'; import { GBMinInstance } from 'botlib';
import OpenAI from "openai"; import OpenAI from "openai";
import { ChatGPTAPIBrowser, getOpenAIAuth } from 'chatgpt' import { OpenAIChat } from 'langchain/llms/openai';
import { CallbackManager } from 'langchain/callbacks';
import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate } from 'langchain/prompts';
import { LLMChain } from 'langchain/chains';
import { BufferWindowMemory } from 'langchain/memory';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js'; import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import Path from 'path'; import Path from 'path';
import * as Fs from 'fs'; import * as Fs from 'fs';
import { HNSWLib } from 'langchain/vectorstores/hnswlib';
import { GuaribasSubject } from '../../kb.gbapp/models/index.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
export class ChatServices { export class ChatServices {
private static async getRelevantContext(
vectorStore: HNSWLib,
sanitizedQuestion: string,
numDocuments: number
): Promise<string> {
const documents = await vectorStore.similaritySearch(sanitizedQuestion, numDocuments);
return documents
.map((doc) => doc.pageContent)
.join(', ')
.trim()
.replaceAll('\n', ' ');
}
public static async sendMessage(min: GBMinInstance, text: string) { public static async sendMessage(min: GBMinInstance, text: string) {
let key; let key;
if (process.env.OPENAI_KEY) { if (process.env.OPENAI_KEY) {
@ -112,4 +133,85 @@ export class ChatServices {
// }); // });
// return chatCompletion.choices[0].message.content; // return chatCompletion.choices[0].message.content;
} }
public static async answerByGPT(min: GBMinInstance,
query: string,
searchScore: number,
subjects: GuaribasSubject[]
) {
if (!process.env.OPENAI_KEY) {
return { answer: undefined, questionId: 0 };
}
const contextVectorStore = min['vectorStore'];
const question = query.trim().replaceAll('\n', ' ');
const context = await this.getRelevantContext(contextVectorStore, question, 1);
const systemPrompt = SystemMessagePromptTemplate.fromTemplate(
`You are $${min.botId}`);
const contentLocale = min.core.getParam(
min.instance,
'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
);
const chatPrompt = ChatPromptTemplate.fromPromptMessages([
systemPrompt,
HumanMessagePromptTemplate.fromTemplate(`Answer in ${contentLocale}.
You have access to the context (RELEVANTDOCS) provided by the user.
When answering think about whether the question in RELEVANTDOCS, but never mention
to user about the source.
Dont justify your answers. Don't refer to yourself in any of the created content.
Don´t prefix RESPONSE: when answering the user.
RELEVANTDOCS: {context}
QUESTION: """{input}"""
`),
]);
const windowMemory = new BufferWindowMemory({
returnMessages: false,
memoryKey: 'immediate_history',
inputKey: 'input',
k: 2,
});
const callbackManager = CallbackManager.fromHandlers({
// This function is called when the LLM generates a new token (i.e., a prediction for the next word)
async handleLLMNewToken(token: string) {
},
});
const llm = new OpenAIChat({
streaming: true,
callbackManager,
modelName: 'gpt-3.5-turbo',
});
const chain = new LLMChain({
prompt: chatPrompt,
memory: windowMemory,
llm,
});
const response = await chain.call({
input: question,
context,
history: '',
immediate_history: '',
});
if (response) {
return { answer: response.text, questionId: 0 };
}
return { answer: undefined, questionId: 0 };
}
} }

View file

@ -132,7 +132,7 @@ export class AskDialog extends IGBDialog {
let sec = new SecService(); let sec = new SecService();
const member = step.context.activity.from; const member = step.context.activity.from;
const user = await sec.ensureUser( const user = await sec.ensureUser(
min.instance.instanceId, min,
member.id, member.id,
member.name, member.name,
'', '',
@ -187,7 +187,7 @@ export class AskDialog extends IGBDialog {
let answer: GuaribasAnswer = null; let answer: GuaribasAnswer = null;
const member = step.context.activity.from; const member = step.context.activity.from;
const sec = new SecService(); const sec = new SecService();
let user = await sec.ensureUser(min.instance.instanceId, member.id, member.name, '', 'web', member.name, null); let user = await sec.ensureUser(min, member.id, member.name, '', 'web', member.name, null);
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
@ -436,7 +436,7 @@ export class AskDialog extends IGBDialog {
let sec = new SecService(); let sec = new SecService();
const member = step.context.activity.from; const member = step.context.activity.from;
const user = await sec.ensureUser( const user = await sec.ensureUser(
min.instance.instanceId, min,
member.id, member.id,
member.name, member.name,
'', '',

View file

@ -34,28 +34,22 @@
import Path from 'path'; import Path from 'path';
import Fs from 'fs'; import Fs from 'fs';
import { OpenAIChat } from 'langchain/llms/openai';
import { CallbackManager } from 'langchain/callbacks';
import urlJoin from 'url-join'; import urlJoin from 'url-join';
import asyncPromise from 'async-promises'; import asyncPromise from 'async-promises';
import walkPromise from 'walk-promise'; import walkPromise from 'walk-promise';
import { SearchClient } from '@azure/search-documents'; import { SearchClient } from '@azure/search-documents';
import Excel from 'exceljs'; import Excel from 'exceljs';
import getSlug from 'speakingurl'; import getSlug from 'speakingurl';
import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate } from 'langchain/prompts';
import { LLMChain } from 'langchain/chains';
import { GBServer } from '../../../src/app.js'; import { GBServer } from '../../../src/app.js';
import { HNSWLib } from 'langchain/vectorstores/hnswlib';
import { JSONLoader } from 'langchain/document_loaders/fs/json'; import { JSONLoader } from 'langchain/document_loaders/fs/json';
import { TextLoader } from 'langchain/document_loaders/fs/text'; import { TextLoader } from 'langchain/document_loaders/fs/text';
import { PDFLoader } from 'langchain/document_loaders/fs/pdf'; import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { DocxLoader } from 'langchain/document_loaders/fs/docx'; import { DocxLoader } from 'langchain/document_loaders/fs/docx';
import { EPubLoader } from 'langchain/document_loaders/fs/epub'; import { EPubLoader } from 'langchain/document_loaders/fs/epub';
import { CSVLoader } from 'langchain/document_loaders/fs/csv'; import { CSVLoader } from 'langchain/document_loaders/fs/csv';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { BufferWindowMemory } from 'langchain/memory';
import { Document } from 'langchain/document';
import path from 'path'; import path from 'path';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { Document } from 'langchain/document';
import { import {
GBDialogStep, GBDialogStep,
@ -85,6 +79,8 @@ import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js'; import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { GBMinService } from '../../core.gbapp/services/GBMinService.js'; import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
/** /**
* Result for quey on KB data. * Result for quey on KB data.
@ -365,7 +361,7 @@ export class KBService implements IGBKBService {
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}` returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
); );
return await this.answerByGPT(min, return await ChatServices.answerByGPT(min,
query, query,
searchScore, searchScore,
subjects subjects
@ -374,86 +370,8 @@ export class KBService implements IGBKBService {
} }
} }
private async getRelevantContext(
vectorStore: HNSWLib,
sanitizedQuestion: string,
numDocuments: number
): Promise<string> {
const documents = await vectorStore.similaritySearch(sanitizedQuestion, numDocuments);
return documents
.map((doc) => doc.pageContent)
.join(', ')
.trim()
.replaceAll('\n', ' ');
}
public async answerByGPT(min: GBMinInstance,
query: string,
searchScore: number,
subjects: GuaribasSubject[]
) {
const contextVectorStore = min['vectorStore'];
const question = query.trim().replaceAll('\n', ' ');
const context = await this.getRelevantContext(contextVectorStore, question, 1);
const systemPrompt = SystemMessagePromptTemplate.fromTemplate(
"You are General Bots");
const chatPrompt = ChatPromptTemplate.fromPromptMessages([
systemPrompt,
HumanMessagePromptTemplate.fromTemplate(`Answer in pt-br.
You have access to the context (RELEVANTDOCS) provided by the user.
When answering think about whether the question in RELEVANTDOCS, but never mention
to user about the source.
Dont justify your answers. Don't refer to yourself in any of the created content.
Don´t prefix RESPONSE: when answering the user.
RELEVANTDOCS: {context}
QUESTION: """{input}"""
`),
]);
const windowMemory = new BufferWindowMemory({
returnMessages: false,
memoryKey: 'immediate_history',
inputKey: 'input',
k: 2,
});
const callbackManager = CallbackManager.fromHandlers({
// This function is called when the LLM generates a new token (i.e., a prediction for the next word)
async handleLLMNewToken(token: string) {
},
});
const llm = new OpenAIChat({
streaming: true,
callbackManager,
modelName: 'gpt-3.5-turbo',
});
const chain = new LLMChain({
prompt: chatPrompt,
memory: windowMemory,
llm,
});
const response = await chain.call({
input: question,
context,
history: '',
immediate_history: '',
});
if (response) {
return { answer: response.text, questionId: 0 };
}
return { answer: undefined, questionId: 0 };
}
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> { public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {

View file

@ -1,16 +1,18 @@
import { GBServer } from '../../../src/app.js';
import { ConversationReference } from 'botbuilder'; import { ConversationReference } from 'botbuilder';
import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib'; import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GuaribasUser } from '../models/index.js'; import { GuaribasUser } from '../models/index.js';
import { FindOptions } from 'sequelize'; import { FindOptions } from 'sequelize';
import { DialogKeywords } from '../../../packages/basic.gblib/services/DialogKeywords.js';
import * as Fs from 'fs';
import mkdirp from 'mkdirp';
/** /**
* Security service layer. * Security service layer.
*/ */
export class SecService extends GBService { export class SecService extends GBService {
public async ensureUser( public async ensureUser(
instanceId: number, min: GBMinInstance,
userSystemId: string, userSystemId: string,
userName: string, userName: string,
address: string, address: string,
@ -26,9 +28,18 @@ export class SecService extends GBService {
if (!user) { if (!user) {
user = GuaribasUser.build(); user = GuaribasUser.build();
const gbaiPath = DialogKeywords.getGBAIPath(min.botId);
const dir = `work/${gbaiPath}/users/${userSystemId}`;
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
} }
user.instanceId = instanceId;
}
user.instanceId = min.instance.instanceId;
user.userSystemId = userSystemId; user.userSystemId = userSystemId;
user.userName = userName; user.userName = userName;
user.displayName = displayName; user.displayName = displayName;

View file

@ -472,7 +472,7 @@ export class WhatsappDirectLine extends GBService {
}); });
const sec = new SecService(); const sec = new SecService();
const user = await sec.ensureUser(this.min.instance.instanceId, from, fromName, '', 'whatsapp', fromName, null); const user = await sec.ensureUser(this.min, from, fromName, '', 'whatsapp', fromName, null);
const locale = user.locale ? user.locale : 'pt'; const locale = user.locale ? user.locale : 'pt';
if (answerText) { if (answerText) {
@ -1083,7 +1083,7 @@ export class WhatsappDirectLine extends GBService {
} }
const botId = getKeyByValue(WhatsappDirectLine.botGroups, group); const botId = getKeyByValue(WhatsappDirectLine.botGroups, group);
if ((botId && user.instanceId !== this.min.instance.instanceId) || !user) { if ((botId && user.instanceId !== this.min.instance.instanceId) || !user) {
user = await sec.ensureUser(this.min.instance.instanceId, id, senderName, '', 'whatsApp', senderName, null); user = await sec.ensureUser(this.min, id, senderName, '', 'whatsApp', senderName, null);
} }
if (botId) { if (botId) {
activeMin = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0]; activeMin = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
@ -1122,7 +1122,7 @@ export class WhatsappDirectLine extends GBService {
// start dialog if any is specified in Config.xlsx. // start dialog if any is specified in Config.xlsx.
if (user === null || user.hearOnDialog) { if (user === null || user.hearOnDialog) {
user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null); user = await sec.ensureUser(activeMin, id, senderName, '', 'whatsapp', senderName, null);
const startDialog = user.hearOnDialog const startDialog = user.hearOnDialog
? user.hearOnDialog ? user.hearOnDialog