diff --git a/packages/basic.gblib/services/DialogKeywords.ts b/packages/basic.gblib/services/DialogKeywords.ts index 08dbb525..9a3b16e9 100644 --- a/packages/basic.gblib/services/DialogKeywords.ts +++ b/packages/basic.gblib/services/DialogKeywords.ts @@ -1366,17 +1366,17 @@ export class DialogKeywords { } public async start({ botId, botApiKey, userSystemId, text }) { + let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0]; let sec = new SecService(); let user = await sec.getUserFromSystemId(userSystemId); + if (!user) { user = await sec.ensureUser(min, userSystemId, userSystemId, null, 'api', 'API User', null); } const pid = GBVMService.createProcessInfo(user, min, 'api', null); - const conversation = min['apiConversations'][pid]; - const client = await GBUtil.getDirectLineClient(min); conversation.client = client; const response = await client.apis.Conversations.Conversations_StartConversation(); diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index 083b5741..c13e3dc3 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -217,7 +217,7 @@ export class GBVMService extends GBService { }`; await fs.writeFile(urlJoin(folder, 'package.json'), packageJson); - GBLogEx.info(min, `Installing .gbdialog node_modules for ${min.botId}...`); + GBLogEx.info(min, `Installing node_modules...`); const npmPath = urlJoin(process.env.PWD, 'node_modules', '.bin', 'npm'); child_process.exec(`${npmPath} install`, { cwd: folder }); } @@ -280,7 +280,7 @@ export class GBVMService extends GBService { }; if (!min[connectionName]) { - GBLogEx.info(min, `Loading custom connection ${connectionName}...`); + GBLogEx.info(min, `Loading data connection ${connectionName}...`); min[connectionName] = new Sequelize(storageName, username, password, sequelizeOptions); min[connectionName]['gbconnection'] = con; } @@ -712,7 +712,7 @@ await fs.writeFile(mapFile, JSON.stringify(map)); code = ji.default(code, ' '); await fs.writeFile(jsfile, code); - GBLogEx.info(min, `[GBVMService] Finished loading of ${filename}, JavaScript from Word: \n ${code}`); + GBLogEx.info(min, `Code reloaded: ${path.basename(filename)}.`); } private async executeTasks(min, tasks) { diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index bd9fb3eb..f6364171 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -59,6 +59,7 @@ import { GBLogEx } from './GBLogEx.js'; import { GBUtil } from '../../../src/util.js'; import { HNSWLib } from '@langchain/community/vectorstores/hnswlib'; import { OpenAIEmbeddings } from '@langchain/openai'; +import { GBMinService } from './GBMinService.js'; /** * Deployer service for bots, themes, ai and more. @@ -688,6 +689,7 @@ export class GBDeployer implements IGBDeployer { await this.core.saveInstance(min.instance); GBServer.globals.minService.unmountBot(min.botId); GBServer.globals.minService.mountBot(min.instance); + GBLogEx.info(min, `Bot ${min.botId} reloaded.`); } break; diff --git a/packages/core.gbapp/services/GBLogEx.ts b/packages/core.gbapp/services/GBLogEx.ts index 7641e457..887243ff 100644 --- a/packages/core.gbapp/services/GBLogEx.ts +++ b/packages/core.gbapp/services/GBLogEx.ts @@ -40,48 +40,48 @@ import { GBServer } from '../../../src/app.js'; import { GBConfigService } from './GBConfigService.js'; export class GBLogEx { - public static async error(minOrInstanceId: any, message: string) { + private static async logWithLevel( + level: 'error' | 'debug' | 'info' | 'verbose', + minOrInstanceId: any, + message: string + ) { + const instanceId = this.normalizeInstanceId(minOrInstanceId); + GBLog[level](`${instanceId}: ${message}`); + await this.log(instanceId, level.charAt(0), message); + } + + private static normalizeInstanceId(minOrInstanceId: any): string | number { if (typeof minOrInstanceId === 'object') { - minOrInstanceId = minOrInstanceId.instance.instanceId; + return minOrInstanceId.instance ? minOrInstanceId.instance.botId : minOrInstanceId.botId; } - GBLog.error(`${minOrInstanceId}: ${message}`); - await this.log(minOrInstanceId, 'e', message); + return minOrInstanceId === 0 ? 'default' : minOrInstanceId; + } + + public static async error(minOrInstanceId: any, message: string) { + await this.logWithLevel('error', minOrInstanceId, message); } public static async debug(minOrInstanceId: any, message: string) { - if (typeof minOrInstanceId === 'object') { - minOrInstanceId = minOrInstanceId.instance.instanceId; - } - GBLog.debug(`${minOrInstanceId}: ${message}`); - await this.log(minOrInstanceId, 'd', message); + await this.logWithLevel('debug', minOrInstanceId, message); } public static async info(minOrInstanceId: any, message: string) { - - if (typeof minOrInstanceId === 'object') { - minOrInstanceId = minOrInstanceId.instance.instanceId; - } - GBLog.info(`${minOrInstanceId}: ${message}`); - await this.log(minOrInstanceId, 'i', message); + await this.logWithLevel('info', minOrInstanceId, message); } public static async verbose(minOrInstanceId: any, message: string) { - if (typeof minOrInstanceId === 'object') { - minOrInstanceId = minOrInstanceId.instance.instanceId; - } - GBLog.verbose(`${minOrInstanceId}: ${message}`); - await this.log(minOrInstanceId, 'v', message); + await this.logWithLevel('verbose', minOrInstanceId, message); } /** * Finds and update user agent information to a next available person. */ - public static async log(instance: IGBInstance, kind: string, message: string): Promise { + public static async log(instance, kind: string, message: string): Promise { if (GBConfigService.get('LOG_ON_STORAGE')) { message = message ? message.substring(0, 1023) : null; return await GuaribasLog.create({ - instanceId: instance ? instance.instanceId : GBServer.globals, + instanceId: instance ? instance : 0, message: message, kind: kind }); diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 973724e2..9720e684 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -181,11 +181,11 @@ export class GBMinService { instances, (async instance => { try { - GBLog.info(`Mounting ${instance.botId}...`); + GBLogEx.info(instance, `Mounting...`); const min = await this['mountBot'](instance); minInstances.push(min); } catch (error) { - GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`); + GBLogEx.error(instance, `Error mounting bot: ${error.message}\n${error.stack}`); } }).bind(this) ); @@ -400,6 +400,7 @@ export class GBMinService { res.end(); }); + await GBMinService.ensureAPI(min); GBLog.verbose(`GeneralBots(${instance.engineName}) listening on: ${url}.`); // Generates MS Teams manifest. @@ -480,10 +481,6 @@ export class GBMinService { GBDeployer.mountGBKBAssets(`${botId}.gbkb`, botId, `${botId}.gbkb`); - // Loads API. - - await this.ensureAPI(); - return min; } @@ -1651,9 +1648,15 @@ export class GBMinService { } } - public async ensureAPI() { - const mins = GBServer.globals.minInstances; - + public static async ensureAPI(min: GBMinInstance) { + const api = min.core.getParam(min.instance, 'Server API', null); + if (!api) { + + return; + } + + GBLogEx.info(min, `Enabling API...`); + function getRemoteId(ctx: Koa.Context) { return '1'; // Each bot has its own API. } @@ -1666,7 +1669,7 @@ export class GBMinService { }); } else { resolve(true); - GBLogEx.info(0, 'Loading General Bots API...'); + } }); }; @@ -1674,10 +1677,15 @@ export class GBMinService { await close(); let proxies = {}; - await CollectionUtil.asyncForEach(mins, async min => { - let dialogs = {}; - await CollectionUtil.asyncForEach(Object.values(min.scriptMap), async script => { - dialogs[script] = async data => { + let dialogs = {}; + await CollectionUtil.asyncForEach(Object.values(min.scriptMap), async script => { + dialogs[script] = async data => { + + if (!data.userSystemId){ + throw new Error('UserSystemId is required.'); + } + + let sec = new SecService(); const user = await sec.ensureUser( min, @@ -1706,8 +1714,8 @@ export class GBMinService { ret = pid; } return ret; - }; - }); + + }; const proxy = { dk: new DialogKeywords(), diff --git a/packages/core.gbapp/services/router/bridge.ts b/packages/core.gbapp/services/router/bridge.ts index 07ad1e4b..5d4dc9ba 100644 --- a/packages/core.gbapp/services/router/bridge.ts +++ b/packages/core.gbapp/services/router/bridge.ts @@ -62,6 +62,7 @@ export const getRouter = ( }; router.post('/v3/directline/conversations', reqs); + router.post(`/api/messages/${botId}/v3/directline/conversations`, reqs); router.post(`/directline/${botId}/conversations`, reqs); router.post(`/directline/conversations`, reqs); diff --git a/templates/llm-server.gbai/llm-server.gbdata/products.csv b/templates/llm-server.gbai/llm-server.gbdata/products.csv new file mode 100644 index 00000000..65f286d1 --- /dev/null +++ b/templates/llm-server.gbai/llm-server.gbdata/products.csv @@ -0,0 +1,11 @@ +Product ID,Product Name,Category,Price,Stock Quantity,Expiration Date,Supplier Name,Discount (%),Rating +101,Apple,Fruits,0.50,200,2024-09-20,Fresh Farms,5,4.5 +102,Banana,Fruits,0.30,150,2024-09-15,Green Valley,10,4.7 +103,Carrot,Vegetables,0.20,180,2024-10-10,Organic Harvest,0,4.3 +104,Bread,Bakery,1.00,50,2024-09-12,BakeHouse,15,4.1 +105,Milk,Dairy,1.50,80,2024-09-18,DairyPure,0,4.6 +106,Chicken Breast,Meat,5.00,120,2024-09-25,Farm Fresh,5,4.8 +107,Orange Juice,Beverages,2.00,60,2024-09-22,Fruit Press,10,4.4 +108,Almonds,Snacks,4.00,40,2024-11-30,Nutty Harvest,0,4.7 +109,Rice,Grains,2.50,300,2025-01-01,Whole Foods,0,4.2 +110,Pasta,Grains,1.20,250,2025-02-15,GrainLand,0,4.0 \ No newline at end of file diff --git a/templates/llm-server.gbai/llm-server.gbdialog/start.bas b/templates/llm-server.gbai/llm-server.gbdialog/start.bas index 3ac6d790..480f46d1 100644 --- a/templates/llm-server.gbai/llm-server.gbdialog/start.bas +++ b/templates/llm-server.gbai/llm-server.gbdialog/start.bas @@ -1,34 +1,10 @@ +PARAM operator AS number LIKE 12312312 DESCRIPTION "Operator code." +DESCRIPTION It is a WebService of GB. -PARAM stall AS number LIKE Stall Code -PARAM operator AS number LIKE Operator Code -DESCRIPTION This function (tool) is never called by LLM. It is a WebService of GB. - -REM Login as Waiter -data = NEW OBJECT -data.OperatorIdentifier = operator -data.StallId = stall -login = POST host + "/login", data -SET HEADER "Authorization" AS login.accessToken - -REM Get the menu - Use the token retrieved above. -data = GET host + "/Stall/${stall}" -products = NEW ARRAY - -FOR EACH item IN data[0].items - IF item.itemStatus = "Active" THEN - product = NEW OBJECT - product.id = item.id - product.price = item.price - product.name = item.product.name - product.detail = item.detail - product.sides = item.sideGroups - - products.push(product) - END IF -NEXT +products = FIND "products.csv" BEGIN SYSTEM PROMPT - + You must act as a chatbot that will assist a store attendant by following these rules: Whenever the attendant places an order, it must include the table and the customer's name. Example: A 400ml Pineapple Caipirinha for Rafael at table 10. Orders are based on the products and sides from this product menu: @@ -68,7 +44,6 @@ Here is an example of the Order JSON, clear the items and send one with the orde notes: none } ], - stallId: ${stall}, userId: ${operator}, accountIdentifier: Areia, deliveryTypeId: 2, diff --git a/templates/llm-server.gbai/llm-server.gbot/config.csv b/templates/llm-server.gbai/llm-server.gbot/config.csv new file mode 100644 index 00000000..7d6ba41f --- /dev/null +++ b/templates/llm-server.gbai/llm-server.gbot/config.csv @@ -0,0 +1,4 @@ +name,value +Answer Mode,direct +Server API,true +Start Dialog,start \ No newline at end of file