new(security.gblib): SMS Auth.
This commit is contained in:
parent
158c1603c9
commit
a7f345592a
12 changed files with 341 additions and 96 deletions
|
@ -116,7 +116,7 @@
|
||||||
"botframework-connector": "4.18.0",
|
"botframework-connector": "4.18.0",
|
||||||
"botlib": "3.0.11",
|
"botlib": "3.0.11",
|
||||||
"c3-chart-maker": "0.2.8",
|
"c3-chart-maker": "0.2.8",
|
||||||
"cd": "0.3.3",
|
"cd": "^0.3.3",
|
||||||
"chalk-animation": "^2.0.3",
|
"chalk-animation": "^2.0.3",
|
||||||
"chatgpt": "2.4.2",
|
"chatgpt": "2.4.2",
|
||||||
"chrome-remote-interface": "0.31.3",
|
"chrome-remote-interface": "0.31.3",
|
||||||
|
|
|
@ -466,7 +466,7 @@ export class GBVMService extends GBService {
|
||||||
}
|
}
|
||||||
} while (include);
|
} while (include);
|
||||||
|
|
||||||
let { code, map, metadata, tasks } = await this.convert(filename, mainName, basicCode);
|
let { code, map, metadata, tasks, systemPrompt } = await this.convert(filename, mainName, basicCode);
|
||||||
|
|
||||||
// Generates function JSON metadata to be used later.
|
// Generates function JSON metadata to be used later.
|
||||||
|
|
||||||
|
@ -474,7 +474,6 @@ export class GBVMService extends GBService {
|
||||||
Fs.writeFileSync(jsonFile, JSON.stringify(metadata));
|
Fs.writeFileSync(jsonFile, JSON.stringify(metadata));
|
||||||
|
|
||||||
const mapFile = `${filename}.map`;
|
const mapFile = `${filename}.map`;
|
||||||
|
|
||||||
Fs.writeFileSync(mapFile, JSON.stringify(map));
|
Fs.writeFileSync(mapFile, JSON.stringify(map));
|
||||||
|
|
||||||
// Execute off-line code tasks
|
// Execute off-line code tasks
|
||||||
|
@ -836,6 +835,7 @@ export class GBVMService extends GBService {
|
||||||
let description;
|
let description;
|
||||||
let table = null; // Used for TABLE keyword.
|
let table = null; // Used for TABLE keyword.
|
||||||
let talk = null;
|
let talk = null;
|
||||||
|
let systemPrompt = null;
|
||||||
let connection = null;
|
let connection = null;
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
let fields = {};
|
let fields = {};
|
||||||
|
@ -851,7 +851,7 @@ export class GBVMService extends GBService {
|
||||||
|
|
||||||
line = line.replace(/^\s*\d+\s*/gi, '');
|
line = line.replace(/^\s*\d+\s*/gi, '');
|
||||||
|
|
||||||
if (!table && !talk) {
|
if (!table && !talk && !systemPrompt) {
|
||||||
for (let j = 0; j < keywords.length; j++) {
|
for (let j = 0; j < keywords.length; j++) {
|
||||||
line = line.replace(keywords[j][0], keywords[j][1]); // TODO: Investigate delay here.
|
line = line.replace(keywords[j][0], keywords[j][1]); // TODO: Investigate delay here.
|
||||||
}
|
}
|
||||||
|
@ -874,6 +874,15 @@ export class GBVMService extends GBService {
|
||||||
emmit = false;
|
emmit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const endSystemPromptKeyword = /^\s*END SYSTEM PROMPT\s*/gim;
|
||||||
|
let endSystemPromptReg = endSystemPromptKeyword.exec(line);
|
||||||
|
if (endSystemPromptReg && systemPrompt) {
|
||||||
|
line = systemPrompt + '`})';
|
||||||
|
|
||||||
|
systemPrompt = null;
|
||||||
|
emmit = true;
|
||||||
|
}
|
||||||
|
|
||||||
const endTalkKeyword = /^\s*END TALK\s*/gim;
|
const endTalkKeyword = /^\s*END TALK\s*/gim;
|
||||||
let endTalkReg = endTalkKeyword.exec(line);
|
let endTalkReg = endTalkKeyword.exec(line);
|
||||||
if (endTalkReg && talk) {
|
if (endTalkReg && talk) {
|
||||||
|
@ -883,7 +892,6 @@ export class GBVMService extends GBService {
|
||||||
emmit = true;
|
emmit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const endTableKeyword = /^\s*END TABLE\s*/gim;
|
const endTableKeyword = /^\s*END TABLE\s*/gim;
|
||||||
let endTableReg = endTableKeyword.exec(line);
|
let endTableReg = endTableKeyword.exec(line);
|
||||||
if (endTableReg && table) {
|
if (endTableReg && table) {
|
||||||
|
@ -906,6 +914,13 @@ export class GBVMService extends GBService {
|
||||||
emmit = false;
|
emmit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inside BEGIN SYSTEM PROMPT
|
||||||
|
|
||||||
|
if (systemPrompt) {
|
||||||
|
systemPrompt += line + '\\n';
|
||||||
|
emmit = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Inside BEGIN/END table pair containing FIELDS.
|
// Inside BEGIN/END table pair containing FIELDS.
|
||||||
|
|
||||||
if (table && line.trim() !== '') {
|
if (table && line.trim() !== '') {
|
||||||
|
@ -929,6 +944,13 @@ export class GBVMService extends GBService {
|
||||||
emmit = false;
|
emmit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const systemPromptKeyword = /^\s*BEGIN SYSTEM PROMPT\s*/gim;
|
||||||
|
let systemPromptReg = systemPromptKeyword.exec(line);
|
||||||
|
if (systemPromptReg && !systemPrompt) {
|
||||||
|
systemPrompt = "await sys.setSystemPrompt ({pid: pid, text: `";
|
||||||
|
emmit = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Add additional lines returned from replacement.
|
// Add additional lines returned from replacement.
|
||||||
|
|
||||||
let add = emmit ? line.split(/\r\n|\r|\n/).length : 0;
|
let add = emmit ? line.split(/\r\n|\r|\n/).length : 0;
|
||||||
|
@ -952,7 +974,7 @@ export class GBVMService extends GBService {
|
||||||
|
|
||||||
let metadata = GBVMService.getMetadata(mainName, properties, description);
|
let metadata = GBVMService.getMetadata(mainName, properties, description);
|
||||||
|
|
||||||
return { code, map, metadata, tasks };
|
return { code, map, metadata, tasks, systemPrompt };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1440,6 +1440,20 @@ export class SystemKeywords {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async setSystemPrompt({ pid, systemPrompt }) {
|
||||||
|
|
||||||
|
let { min, user } = await DialogKeywords.getProcessInfo(pid);
|
||||||
|
const sec = new SecService();
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
user['systemPrompt'] = systemPrompt;
|
||||||
|
const path = DialogKeywords.getGBAIPath(min.botId);
|
||||||
|
const systemPromptFile = urlJoin(process.cwd(), 'work', path, 'users',user.userSystemId, 'systemPrompt.txt');
|
||||||
|
Fs.writeFileSync(systemPromptFile, systemPrompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a folder in the bot instance drive.
|
* Creates a folder in the bot instance drive.
|
||||||
*
|
*
|
||||||
|
|
|
@ -68,6 +68,11 @@ import { GBUtil } from '../../../src/util.js';
|
||||||
* services like NLP or Search.
|
* services like NLP or Search.
|
||||||
*/
|
*/
|
||||||
export class GBConversationalService {
|
export class GBConversationalService {
|
||||||
|
|
||||||
|
public async getNewMobileCode(){
|
||||||
|
throw new Error('Method removed.');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the core service.
|
* Reference to the core service.
|
||||||
*/
|
*/
|
||||||
|
@ -280,20 +285,6 @@ export class GBConversationalService {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNewMobileCode() {
|
|
||||||
const passwordGenerator = new PasswordGenerator();
|
|
||||||
const options = {
|
|
||||||
upperCaseAlpha: false,
|
|
||||||
lowerCaseAlpha: false,
|
|
||||||
number: true,
|
|
||||||
specialCharacter: false,
|
|
||||||
minimumLength: 4,
|
|
||||||
maximumLength: 4
|
|
||||||
};
|
|
||||||
let code = passwordGenerator.generatePassword(options);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCurrentLanguage(step: GBDialogStep) {
|
public getCurrentLanguage(step: GBDialogStep) {
|
||||||
return step.context.activity.locale;
|
return step.context.activity.locale;
|
||||||
}
|
}
|
||||||
|
|
|
@ -768,9 +768,9 @@ export class GBMinService {
|
||||||
min['scheduleMap'] = {};
|
min['scheduleMap'] = {};
|
||||||
min['conversationWelcomed'] = {};
|
min['conversationWelcomed'] = {};
|
||||||
if (process.env.OPENAI_API_KEY) {
|
if (process.env.OPENAI_API_KEY) {
|
||||||
min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min);
|
|
||||||
const gbkbPath = DialogKeywords.getGBAIPath(min.botId, 'gbkb');
|
const gbkbPath = DialogKeywords.getGBAIPath(min.botId, 'gbkb');
|
||||||
min['vectorStorePath'] = Path.join('work', gbkbPath, 'docs-vectorized');
|
min['vectorStorePath'] = Path.join('work', gbkbPath, 'docs-vectorized');
|
||||||
|
min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min);
|
||||||
}
|
}
|
||||||
min['apiConversations'] = {};
|
min['apiConversations'] = {};
|
||||||
min.packages = sysPackages;
|
min.packages = sysPackages;
|
||||||
|
|
|
@ -30,30 +30,81 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { RunnableSequence } from "@langchain/core/runnables";
|
|
||||||
import { ChatOpenAI } from "@langchain/openai";
|
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import * as Fs from 'fs';
|
|
||||||
import { formatXml } from "langchain/agents/format_scratchpad/xml";
|
|
||||||
import { jsonSchemaToZod } from "json-schema-to-zod";
|
|
||||||
import { renderTextDescription } from "langchain/tools/render";
|
|
||||||
|
|
||||||
import { AgentExecutor, AgentStep } from "langchain/agents";
|
|
||||||
import { BufferWindowMemory } from 'langchain/memory';
|
|
||||||
import { AIMessagePromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
|
|
||||||
import { Tool } from "langchain/tools";
|
|
||||||
import { HNSWLib } from '@langchain/community/vectorstores/hnswlib';
|
import { HNSWLib } from '@langchain/community/vectorstores/hnswlib';
|
||||||
|
import { StringOutputParser } from "@langchain/core/output_parsers";
|
||||||
|
import { AIMessagePromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
|
||||||
|
import { RunnableSequence } from "@langchain/core/runnables";
|
||||||
|
import { convertToOpenAITool } from "@langchain/core/utils/function_calling";
|
||||||
|
import { ChatOpenAI } from "@langchain/openai";
|
||||||
|
import { GBLog, GBMinInstance } from 'botlib';
|
||||||
|
import * as Fs from 'fs';
|
||||||
|
import { jsonSchemaToZod } from "json-schema-to-zod";
|
||||||
|
import { BufferWindowMemory } from 'langchain/memory';
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
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 { GBVMService } from '../../basic.gblib/services/GBVMService.js';
|
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
|
||||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
|
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
|
||||||
import { GuaribasSubject } from '../../kb.gbapp/models/index.js';
|
import { GuaribasSubject } from '../../kb.gbapp/models/index.js';
|
||||||
import { XMLAgentOutputParser } from "langchain/agents/xml/output_parser";
|
import { z } from "zod";
|
||||||
import { StringOutputParser } from "@langchain/core/output_parsers";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { convertToOpenAITool } from "@langchain/core/utils/function_calling";
|
import { JsonOutputToolsParser } from "langchain/output_parsers";
|
||||||
|
import {
|
||||||
|
RunnableLambda,
|
||||||
|
RunnablePassthrough,
|
||||||
|
} from "@langchain/core/runnables";
|
||||||
|
import {
|
||||||
|
CombiningOutputParser,
|
||||||
|
} from "langchain/output_parsers";
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
BaseLLMOutputParser,
|
||||||
|
OutputParserException,
|
||||||
|
} from "@langchain/core/output_parsers";
|
||||||
|
import { ChatGeneration, Generation } from "@langchain/core/outputs";
|
||||||
|
|
||||||
|
export interface CustomOutputParserFields { }
|
||||||
|
|
||||||
|
// This can be more generic, like Record<string, string>
|
||||||
|
export type ExpectedOutput = {
|
||||||
|
greeting: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isChatGeneration(
|
||||||
|
llmOutput: ChatGeneration | Generation
|
||||||
|
): llmOutput is ChatGeneration {
|
||||||
|
return "message" in llmOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CustomLLMOutputParser extends BaseLLMOutputParser<ExpectedOutput> {
|
||||||
|
lc_namespace = ["langchain", "output_parsers"];
|
||||||
|
|
||||||
|
constructor(fields?: CustomOutputParserFields) {
|
||||||
|
super(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseResult(
|
||||||
|
llmOutputs: ChatGeneration[] | Generation[]
|
||||||
|
): Promise<ExpectedOutput> {
|
||||||
|
if (!llmOutputs.length) {
|
||||||
|
throw new OutputParserException(
|
||||||
|
"Output parser did not receive any generations."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let parsedOutput;
|
||||||
|
|
||||||
|
|
||||||
|
if (isChatGeneration(llmOutputs[0])) {
|
||||||
|
parsedOutput = llmOutputs[0].message.content;
|
||||||
|
} else {
|
||||||
|
parsedOutput = llmOutputs[0].text;
|
||||||
|
}
|
||||||
|
let parsedText;
|
||||||
|
parsedText = parsedOutput;
|
||||||
|
return parsedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ChatServices {
|
export class ChatServices {
|
||||||
|
|
||||||
|
@ -62,6 +113,10 @@ export class ChatServices {
|
||||||
sanitizedQuestion: string,
|
sanitizedQuestion: string,
|
||||||
numDocuments: number
|
numDocuments: number
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
if (sanitizedQuestion === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
const documents = await vectorStore.similaritySearch(sanitizedQuestion, numDocuments);
|
const documents = await vectorStore.similaritySearch(sanitizedQuestion, numDocuments);
|
||||||
return documents
|
return documents
|
||||||
.map((doc) => doc.pageContent)
|
.map((doc) => doc.pageContent)
|
||||||
|
@ -78,13 +133,13 @@ export class ChatServices {
|
||||||
* result = CONTINUE text
|
* result = CONTINUE text
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static async continue(min: GBMinInstance, text: string, chatId) {
|
public static async continue(min: GBMinInstance, question: string, chatId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async answerByGPT(min: GBMinInstance, pid,
|
public static async answerByGPT(min: GBMinInstance, user, pid,
|
||||||
query: string,
|
question: string,
|
||||||
searchScore: number,
|
searchScore: number,
|
||||||
subjects: GuaribasSubject[]
|
subjects: GuaribasSubject[]
|
||||||
) {
|
) {
|
||||||
|
@ -93,6 +148,7 @@ export class ChatServices {
|
||||||
return { answer: undefined, questionId: 0 };
|
return { answer: undefined, questionId: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const contentLocale = min.core.getParam(
|
const contentLocale = min.core.getParam(
|
||||||
min.instance,
|
min.instance,
|
||||||
'Default Content Language',
|
'Default Content Language',
|
||||||
|
@ -102,6 +158,10 @@ export class ChatServices {
|
||||||
let tools = await ChatServices.getTools(min);
|
let tools = await ChatServices.getTools(min);
|
||||||
let toolsAsText = ChatServices.getToolsAsText(tools);
|
let toolsAsText = ChatServices.getToolsAsText(tools);
|
||||||
|
|
||||||
|
const toolMap: Record<string, any> = {
|
||||||
|
multiply: tools[0]
|
||||||
|
};
|
||||||
|
|
||||||
const memory = new BufferWindowMemory({
|
const memory = new BufferWindowMemory({
|
||||||
returnMessages: true,
|
returnMessages: true,
|
||||||
memoryKey: 'chat_history',
|
memoryKey: 'chat_history',
|
||||||
|
@ -114,47 +174,56 @@ export class ChatServices {
|
||||||
modelName: "gpt-3.5-turbo-0125",
|
modelName: "gpt-3.5-turbo-0125",
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const contextVectorStore = min['vectorStore'];
|
|
||||||
|
|
||||||
let promptTemplate = `Answer in ${contentLocale}.
|
const context = min['vectorStore'];
|
||||||
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.
|
|
||||||
Don’t 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}"""
|
|
||||||
|
|
||||||
You have the following tools that you can invoke based on the user inquiry.
|
|
||||||
Tools:
|
|
||||||
|
|
||||||
${toolsAsText}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
|
||||||
const toolMap: Record<string, any> = {
|
|
||||||
multiply: ()=>{},
|
|
||||||
};
|
|
||||||
|
|
||||||
const modelWithTools = model.bind({
|
const modelWithTools = model.bind({
|
||||||
tools: tools.map(convertToOpenAITool),
|
tools: tools.map(convertToOpenAITool),
|
||||||
|
tool_choice: {
|
||||||
|
type: "function",
|
||||||
|
function: { name: "multiply" },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Function for dynamically constructing the end of the chain based on the model-selected tool.
|
||||||
|
const callSelectedTool = RunnableLambda.from(
|
||||||
|
(toolInvocation: Record<string, any>) => {
|
||||||
|
const selectedTool = toolMap[toolInvocation.type];
|
||||||
|
if (!selectedTool) {
|
||||||
|
throw new Error(
|
||||||
|
`No matching tool available for requested type "${toolInvocation.type}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const toolCallChain = RunnableSequence.from([
|
||||||
|
(toolInvocation) => toolInvocation.args,
|
||||||
|
selectedTool,
|
||||||
|
]);
|
||||||
|
// We use `RunnablePassthrough.assign` here to return the intermediate `toolInvocation` params
|
||||||
|
// as well, but you can omit if you only care about the answer.
|
||||||
|
return RunnablePassthrough.assign({
|
||||||
|
output: toolCallChain,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([
|
const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([
|
||||||
AIMessagePromptTemplate.fromTemplate(
|
AIMessagePromptTemplate.fromTemplate(
|
||||||
"Given the following conversation about a codebase and a follow up question, rephrase the follow up question to be a standalone question."
|
"Given the following conversation about a codebase and a follow up question, rephrase the follow up question to be a standalone question."
|
||||||
),
|
),
|
||||||
new MessagesPlaceholder("chat_history"),
|
new MessagesPlaceholder("chat_history"),
|
||||||
AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question} Standalone question:`),
|
AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question}
|
||||||
|
Standalone question:`),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const combineDocumentsPrompt = ChatPromptTemplate.fromMessages([
|
const combineDocumentsPrompt = ChatPromptTemplate.fromMessages([
|
||||||
AIMessagePromptTemplate.fromTemplate(
|
AIMessagePromptTemplate.fromTemplate(
|
||||||
"Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\n\n{context}\n\n"
|
`Use the following pieces of context to answer the question at the end.
|
||||||
|
If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||||
|
\n\n{context}\n\n
|
||||||
|
You have the following tools to call:
|
||||||
|
${toolsAsText}`
|
||||||
|
|
||||||
),
|
),
|
||||||
new MessagesPlaceholder("chat_history"),
|
new MessagesPlaceholder("chat_history"),
|
||||||
HumanMessagePromptTemplate.fromTemplate("Question: {question}"),
|
HumanMessagePromptTemplate.fromTemplate("Question: {question}"),
|
||||||
|
@ -168,15 +237,15 @@ export class ChatServices {
|
||||||
return chat_history;
|
return chat_history;
|
||||||
},
|
},
|
||||||
context: async (output: string) => {
|
context: async (output: string) => {
|
||||||
|
return await ChatServices.getRelevantContext(context, output, 1);
|
||||||
return await this.getRelevantContext(contextVectorStore, output, 1);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
combineDocumentsPrompt,
|
combineDocumentsPrompt,
|
||||||
modelWithTools,
|
modelWithTools,
|
||||||
new StringOutputParser(),
|
new CustomLLMOutputParser(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const conversationalQaChain = RunnableSequence.from([
|
const conversationalQaChain = RunnableSequence.from([
|
||||||
{
|
{
|
||||||
question: (i: { question: string }) => i.question,
|
question: (i: { question: string }) => i.question,
|
||||||
|
@ -190,20 +259,31 @@ export class ChatServices {
|
||||||
new StringOutputParser(),
|
new StringOutputParser(),
|
||||||
combineDocumentsChain,
|
combineDocumentsChain,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const systemPrompt = user['systemPrompt'];
|
||||||
|
|
||||||
const question = "How can I initialize a ReAct agent?";
|
|
||||||
let result = await conversationalQaChain.invoke({
|
let result = await conversationalQaChain.invoke({
|
||||||
question,
|
question,
|
||||||
});
|
});
|
||||||
|
// await memory.saveContext(
|
||||||
return { answer: result.toString() , questionId: 0 };
|
// {
|
||||||
|
// input: query,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// output: result,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
GBLog.info(`GPT Result: ${result.toString()}`);
|
||||||
|
return { answer: result.toString(), questionId: 0 };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static getToolsAsText(tools) {
|
private static getToolsAsText(tools) {
|
||||||
return Object.keys(tools)
|
return Object.keys(tools)
|
||||||
.map((toolname) => `${tools[toolname].function.name}: ${tools[toolname].function.description}`)
|
.map((toolname) => `${tools[toolname].name}: ${tools[toolname].description}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,19 +297,35 @@ export class ChatServices {
|
||||||
|
|
||||||
if (Fs.existsSync(functionJSON)) {
|
if (Fs.existsSync(functionJSON)) {
|
||||||
const func = JSON.parse(Fs.readFileSync(functionJSON, 'utf8'));
|
const func = JSON.parse(Fs.readFileSync(functionJSON, 'utf8'));
|
||||||
func.schema = jsonSchemaToZod(func.properties, { module: "esm" });
|
func.schema = jsonSchemaToZod(func.properties, { module: "esm" });
|
||||||
func.func = async ()=>{
|
func.func = async () => {
|
||||||
const name = '';
|
const name = '';
|
||||||
const pid = 1;
|
const pid = 1;
|
||||||
const text = ''; // TODO:
|
const text = ''; // TODO:
|
||||||
const result = await GBVMService.callVM(name, min, false, pid,false, [text]);
|
const result = await GBVMService.callVM(name, min, false, pid, false, [text]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
functions.push(func);
|
functions.push(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const multiplyTool = new DynamicStructuredTool({
|
||||||
|
name: "multiply",
|
||||||
|
description: "Multiply two integers together.",
|
||||||
|
schema: z.object({
|
||||||
|
firstInt: z.number(),
|
||||||
|
secondInt: z.number(),
|
||||||
|
}),
|
||||||
|
func: async ({ firstInt, secondInt }) => {
|
||||||
|
return (firstInt * secondInt).toString();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
functions.push(multiplyTool);
|
||||||
|
|
||||||
|
|
||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,14 @@ export class AskDialog extends IGBDialog {
|
||||||
min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore
|
min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore
|
||||||
);
|
);
|
||||||
|
|
||||||
const results = await service.ask(min,step.context.activity['pid'], text, searchScore, null /* user.subjects */);
|
// Tries to answer by NLP.
|
||||||
|
|
||||||
|
let handled = await min.conversationalService.routeNLP(step, min, text);
|
||||||
|
if (handled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await service.ask(min, user, step, step.context.activity['pid'], text, searchScore, null /* user.subjects */);
|
||||||
|
|
||||||
// If there is some result, answer immediately.
|
// If there is some result, answer immediately.
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,8 @@ export class KBService implements IGBKBService {
|
||||||
|
|
||||||
public async ask(
|
public async ask(
|
||||||
min: GBMinInstance,
|
min: GBMinInstance,
|
||||||
|
user,
|
||||||
|
step,
|
||||||
pid,
|
pid,
|
||||||
query: string,
|
query: string,
|
||||||
searchScore: number,
|
searchScore: number,
|
||||||
|
@ -372,11 +374,11 @@ export class KBService implements IGBKBService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBLog.info(
|
GBLog.info(
|
||||||
`SEARCH returned LOW level score, calling GPT
|
`SEARCH returned LOW level score, calling NLP if any,
|
||||||
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
|
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return await ChatServices.answerByGPT(min,pid,
|
return await ChatServices.answerByGPT(min, user, pid,
|
||||||
query,
|
query,
|
||||||
searchScore,
|
searchScore,
|
||||||
subjects
|
subjects
|
||||||
|
@ -384,10 +386,6 @@ export class KBService implements IGBKBService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
||||||
const where = { parentSubjectId: parentId, instanceId: instanceId };
|
const where = { parentSubjectId: parentId, instanceId: instanceId };
|
||||||
|
|
||||||
|
|
103
packages/security.gbapp/dialogs/SMSAuthDialog.ts
Normal file
103
packages/security.gbapp/dialogs/SMSAuthDialog.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*****************************************************************************\
|
||||||
|
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||||
|
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||||
|
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||||
|
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||||
|
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||||
|
| |
|
||||||
|
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
|
||||||
|
| Licensed under the AGPL-3.0. |
|
||||||
|
| |
|
||||||
|
| According to our dual licensing model, this program can be used either |
|
||||||
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
|
| or under a proprietary license. |
|
||||||
|
| |
|
||||||
|
| The texts of the GNU Affero General Public License with an additional |
|
||||||
|
| permission and of our proprietary license can be found at and |
|
||||||
|
| in the LICENSE file you have received along with this program. |
|
||||||
|
| |
|
||||||
|
| This program is distributed in the hope that it will be useful, |
|
||||||
|
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
|
||||||
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
|
| GNU Affero General Public License for more details. |
|
||||||
|
| |
|
||||||
|
| "General Bots" is a registered trademark of pragmatismo.com.br. |
|
||||||
|
| The licensing of the program under the AGPLv3 does not imply a |
|
||||||
|
| trademark license. Therefore any rights, title and interest in |
|
||||||
|
| our trademarks remain entirely with us. |
|
||||||
|
| |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview Dialog for handling OAuth scenarios.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { TokenResponse } from 'botbuilder';
|
||||||
|
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
|
||||||
|
import { Messages } from '../strings.js';
|
||||||
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
|
||||||
|
import { SecService } from '../services/SecService.js';
|
||||||
|
/**
|
||||||
|
* Dialogs for handling Menu control.
|
||||||
|
*/
|
||||||
|
export class SMSAuthDialog extends IGBDialog {
|
||||||
|
public static getSMSAuthDialog(min: GBMinInstance) {
|
||||||
|
return {
|
||||||
|
id: '/smsauth',
|
||||||
|
waterfall: [
|
||||||
|
async (step) => {
|
||||||
|
const msg = 'Por favor, qual o seu celular? Ex: 55 21 99999-0000.';
|
||||||
|
step.activeDialog.state.resetInfo = {};
|
||||||
|
return await min.conversationalService.prompt(min, step, msg);
|
||||||
|
|
||||||
|
},
|
||||||
|
async (step) => {
|
||||||
|
|
||||||
|
await step.context.sendActivity('Por favor, digite o código enviado para seu celular.');
|
||||||
|
|
||||||
|
const mobile = step.result.replace(/\+|\s|\-/g, '');
|
||||||
|
const locale = step.context.activity.locale;
|
||||||
|
step.activeDialog.state.resetInfo.mobile = mobile;
|
||||||
|
|
||||||
|
// Generates a new mobile code.
|
||||||
|
|
||||||
|
let code = GBAdminService.getMobileCode();
|
||||||
|
GBLog.info(`SMS Auth: Generated new code: ${code} is being sent.`);
|
||||||
|
step.activeDialog.state.resetInfo.sentCode = code;
|
||||||
|
step.activeDialog.state.resetInfo.mobile = mobile;
|
||||||
|
|
||||||
|
// Sends a confirmation SMS.
|
||||||
|
|
||||||
|
await min.whatsAppDirectLine.sendToDevice(
|
||||||
|
mobile,
|
||||||
|
Messages[locale].please_use_code(code)
|
||||||
|
);
|
||||||
|
return await min.conversationalService.prompt(min, step, Messages[locale].confirm_mobile);
|
||||||
|
},
|
||||||
|
async (step) => {
|
||||||
|
const typed = step.result;
|
||||||
|
const locale = step.context.activity.locale;
|
||||||
|
|
||||||
|
// Checks if the typed code is equal to the one
|
||||||
|
// sent to the registered mobile.
|
||||||
|
|
||||||
|
if (typed == step.activeDialog.state.resetInfo.sentCode) {
|
||||||
|
let sec = new SecService();
|
||||||
|
const member = step.context.activity.from;
|
||||||
|
|
||||||
|
GBLog.info(`SMS Auth: User Authenticated.`);
|
||||||
|
await step.context.sendActivity(Messages[locale].authenticated);
|
||||||
|
|
||||||
|
return await step.endDialog(step.activeDialog.state.resetInfo.mobile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await step.context.sendActivity(Messages[locale].not_authorized);
|
||||||
|
return await step.endDialog(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ import { Sequelize } from 'sequelize-typescript';
|
||||||
import { OAuthDialog } from './dialogs/OAuthDialog.js';
|
import { OAuthDialog } from './dialogs/OAuthDialog.js';
|
||||||
import { ProfileDialog } from './dialogs/ProfileDialog.js';
|
import { ProfileDialog } from './dialogs/ProfileDialog.js';
|
||||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models/index.js';
|
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models/index.js';
|
||||||
|
import { SMSAuthDialog } from './dialogs/SMSAuthDialog.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package for the security module.
|
* Package for the security module.
|
||||||
|
@ -50,9 +51,10 @@ export class GBSecurityPackage implements IGBPackage {
|
||||||
ProfileDialog.getNameDialog(min),
|
ProfileDialog.getNameDialog(min),
|
||||||
ProfileDialog.getEmailDialog(min),
|
ProfileDialog.getEmailDialog(min),
|
||||||
ProfileDialog.getMobileDialog(min),
|
ProfileDialog.getMobileDialog(min),
|
||||||
ProfileDialog.getMobileConfirmDialog(min)
|
ProfileDialog.getMobileConfirmDialog(min),
|
||||||
|
SMSAuthDialog.getSMSAuthDialog(min)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.ENABLE_AUTH) {
|
if (process.env.ENABLE_AUTH) {
|
||||||
out.push(OAuthDialog.getOAuthDialog(min));
|
out.push(OAuthDialog.getOAuthDialog(min));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { FindOptions } from 'sequelize';
|
||||||
import { DialogKeywords } from '../../../packages/basic.gblib/services/DialogKeywords.js';
|
import { DialogKeywords } from '../../../packages/basic.gblib/services/DialogKeywords.js';
|
||||||
import * as Fs from 'fs';
|
import * as Fs from 'fs';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
|
import urlJoin from 'url-join';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security service layer.
|
* Security service layer.
|
||||||
|
@ -26,17 +28,19 @@ export class SecService extends GBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const gbaiPath = DialogKeywords.getGBAIPath(min.botId);
|
||||||
|
const dir = urlJoin ('work',gbaiPath, 'users', userSystemId);
|
||||||
|
|
||||||
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)) {
|
if (!Fs.existsSync(dir)) {
|
||||||
mkdirp.sync(dir);
|
mkdirp.sync(dir);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemPromptFile = urlJoin(dir, 'systemPrompt.txt');
|
||||||
|
if (Fs.existsSync(systemPromptFile)) {
|
||||||
|
user[ 'systemPrompt'] = Fs.readFileSync(systemPromptFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.instanceId = min.instance.instanceId;
|
user.instanceId = min.instance.instanceId;
|
||||||
|
|
|
@ -6,7 +6,11 @@ export const Messages = {
|
||||||
whats_email: "What's your E-mail address?",
|
whats_email: "What's your E-mail address?",
|
||||||
validation_enter_name: 'Please enter your full name.',
|
validation_enter_name: 'Please enter your full name.',
|
||||||
validation_enter_valid_mobile: 'Please enter a valid mobile number.',
|
validation_enter_valid_mobile: 'Please enter a valid mobile number.',
|
||||||
validation_enter_valid_email: 'Please enter a valid e-mail.'
|
validation_enter_valid_email: 'Please enter a valid e-mail.',
|
||||||
|
authenticated: 'You are now authenticated.',
|
||||||
|
not_authorized: 'Wrong verification code. Not authenticated yet. Try again, please.',
|
||||||
|
please_use_code:(code)=> `Please, answer the Bot with the code: ${code}.`
|
||||||
|
|
||||||
},
|
},
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
whats_name: 'Qual o seu nome?',
|
whats_name: 'Qual o seu nome?',
|
||||||
|
@ -18,6 +22,10 @@ export const Messages = {
|
||||||
código enviado para seu celular.`,
|
código enviado para seu celular.`,
|
||||||
validation_enter_valid_email: 'Por favor, digite um e-mail válido no formato nome@domínio.com.br.',
|
validation_enter_valid_email: 'Por favor, digite um e-mail válido no formato nome@domínio.com.br.',
|
||||||
validation_enter_name: 'Por favor, digite seu nome completo',
|
validation_enter_name: 'Por favor, digite seu nome completo',
|
||||||
validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).'
|
validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).',
|
||||||
|
authenticated: 'Você está autenticada(o).',
|
||||||
|
not_authorized: 'Código de identificação inválido. Não autorizado, tente novamente, por favor.',
|
||||||
|
please_use_code:(code)=> `Por favor, responda ao bot com o código: ${code}.`
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue