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();
const member = step.context.activity.from;
const user = await sec.ensureUser(
min.instance.instanceId,
min,
member.id,
member.name,
'',

View file

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

View file

@ -328,6 +328,10 @@ export class GBMinService {
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
}
dir = `work/${gbai}/users`;
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
}
// Loads Named Entity data for this bot.
@ -956,7 +960,7 @@ export class GBMinService {
const member = context.activity.from;
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 params = user.params ? JSON.parse(user.params) : {};
@ -1351,7 +1355,7 @@ export class GBMinService {
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 params = user.params ? JSON.parse(user.params) : {};

View file

@ -88,7 +88,7 @@ export class FeedbackDialog extends IGBDialog {
if (args && args.to) {
// 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 min.conversationalService.sendText(

View file

@ -137,7 +137,7 @@ export class GoogleChatDirectLine extends GBService {
message.ack();
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);

View file

@ -32,14 +32,35 @@
import { GBMinInstance } from 'botlib';
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 { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import Path from 'path';
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 {
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) {
let key;
if (process.env.OPENAI_KEY) {
@ -112,4 +133,85 @@ export class ChatServices {
// });
// 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();
const member = step.context.activity.from;
const user = await sec.ensureUser(
min.instance.instanceId,
min,
member.id,
member.name,
'',
@ -187,7 +187,7 @@ export class AskDialog extends IGBDialog {
let answer: GuaribasAnswer = null;
const member = step.context.activity.from;
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;
@ -436,7 +436,7 @@ export class AskDialog extends IGBDialog {
let sec = new SecService();
const member = step.context.activity.from;
const user = await sec.ensureUser(
min.instance.instanceId,
min,
member.id,
member.name,
'',

View file

@ -34,28 +34,22 @@
import Path from 'path';
import Fs from 'fs';
import { OpenAIChat } from 'langchain/llms/openai';
import { CallbackManager } from 'langchain/callbacks';
import urlJoin from 'url-join';
import asyncPromise from 'async-promises';
import walkPromise from 'walk-promise';
import { SearchClient } from '@azure/search-documents';
import Excel from 'exceljs';
import getSlug from 'speakingurl';
import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate } from 'langchain/prompts';
import { LLMChain } from 'langchain/chains';
import { GBServer } from '../../../src/app.js';
import { HNSWLib } from 'langchain/vectorstores/hnswlib';
import { JSONLoader } from 'langchain/document_loaders/fs/json';
import { TextLoader } from 'langchain/document_loaders/fs/text';
import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { DocxLoader } from 'langchain/document_loaders/fs/docx';
import { EPubLoader } from 'langchain/document_loaders/fs/epub';
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 { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { Document } from 'langchain/document';
import {
GBDialogStep,
@ -85,6 +79,8 @@ import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
/**
* Result for quey on KB data.
@ -365,7 +361,7 @@ export class KBService implements IGBKBService {
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
);
return await this.answerByGPT(min,
return await ChatServices.answerByGPT(min,
query,
searchScore,
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[]> {

View file

@ -1,16 +1,18 @@
import { GBServer } from '../../../src/app.js';
import { ConversationReference } from 'botbuilder';
import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GuaribasUser } from '../models/index.js';
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.
*/
export class SecService extends GBService {
public async ensureUser(
instanceId: number,
min: GBMinInstance,
userSystemId: string,
userName: string,
address: string,
@ -26,9 +28,18 @@ export class SecService extends GBService {
if (!user) {
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.userName = userName;
user.displayName = displayName;

View file

@ -472,7 +472,7 @@ export class WhatsappDirectLine extends GBService {
});
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';
if (answerText) {
@ -1083,7 +1083,7 @@ export class WhatsappDirectLine extends GBService {
}
const botId = getKeyByValue(WhatsappDirectLine.botGroups, group);
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) {
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.
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
? user.hearOnDialog