diff --git a/.vscode/launch.json b/.vscode/launch.json index 817a5373..1e5af120 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,7 @@ "NODE_NO_WARNINGS":"1" }, "args": [ + "--max-old-space-size 5120", "--no-deprecation", "--loader ts-node/esm", "--require ${workspaceRoot}/suppress-node-warnings.cjs", diff --git a/package.json b/package.json index 5db42b0a..9efae544 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "cli-progress": "3.12.0", "cli-spinner": "0.2.10", "core-js": "3.37.1", - "csv-database": "^0.9.2", + "csv-database": "0.9.2", "data-forge": "1.10.2", "date-diff": "1.0.2", "docximager": "0.0.4", diff --git a/packages/core.gbapp/services/GBCoreService.ts b/packages/core.gbapp/services/GBCoreService.ts index cf3cf2ca..9aa69639 100644 --- a/packages/core.gbapp/services/GBCoreService.ts +++ b/packages/core.gbapp/services/GBCoreService.ts @@ -65,6 +65,7 @@ import { GBLogEx } from './GBLogEx.js'; import { GBDeployer } from './GBDeployer.js'; import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js'; import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js'; +import csvdb from 'csv-database'; /** * GBCoreService contains main logic for handling storage services related @@ -133,11 +134,9 @@ export class GBCoreService implements IGBCoreService { } else if (this.dialect === 'sqlite') { storage = GBConfigService.get('STORAGE_FILE'); - if (!Fs.existsSync(storage)){ + if (!Fs.existsSync(storage)) { process.env.STORAGE_SYNC = 'true'; } - - } else { throw new Error(`Unknown dialect: ${this.dialect}.`); } @@ -519,8 +518,7 @@ ENDPOINT_UPDATE=true * Verifies that an complex global password has been specified * before starting the server. */ - public ensureAdminIsSecured() { - } + public ensureAdminIsSecured() {} public async createBootInstance( core: GBCoreService, @@ -672,49 +670,61 @@ ENDPOINT_UPDATE=true } public async setConfig(min, name: string, value: any): Promise { - // Handles calls for BASIC persistence on sheet files. + if (GBConfigService.get('STORAGE_NAME')) { + // Handles calls for BASIC persistence on sheet files. - GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`); + GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`); - let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min); + let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min); - const maxLines = 512; - const file = 'Config.xlsx'; - const path = DialogKeywords.getGBAIPath(min.botId, `gbot`); + const maxLines = 512; + const file = 'Config.xlsx'; + const path = DialogKeywords.getGBAIPath(min.botId, `gbot`); - let document = await new SystemKeywords().internalGetDocument(client, baseUrl, path, file); + let document = await new SystemKeywords().internalGetDocument(client, baseUrl, path, file); - // Creates book session that will be discarded. + // Creates book session that will be discarded. - let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get(); + let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get(); - let results = await client - .api( - `${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')` - ) - .get(); + let results = await client + .api( + `${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')` + ) + .get(); - const rows = results.text; - let address = ''; + const rows = results.text; + let address = ''; - // Fills the row variable. + // Fills the row variable. - for (let i = 1; i <= rows.length; i++) { - let result = rows[i - 1][0]; - if (result && result.toLowerCase() === name.toLowerCase()) { - address = `B${i}:B${i}`; - break; + for (let i = 1; i <= rows.length; i++) { + let result = rows[i - 1][0]; + if (result && result.toLowerCase() === name.toLowerCase()) { + address = `B${i}:B${i}`; + break; + } + } + + let body = { values: [[]] }; + body.values[0][0] = value; + + await client + .api( + `${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')` + ) + .patch(body); + } else { + let path = DialogKeywords.getGBAIPath(min.botId, `gbot`); + const config = Path.join(GBConfigService.get('STORAGE_LIBRARY'), path, 'config.csv'); + + const db = await csvdb(config, ['name', 'value'], ','); + if (await db.get({ name: name })) { + await db.edit({ name: name }, { name, value }); + } else { + await db.add({ name, value }); } } - - let body = { values: [[]] }; - body.values[0][0] = value; - - await client - .api( - `${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')` - ) - .patch(body); } /** @@ -724,7 +734,7 @@ ENDPOINT_UPDATE=true * @param name Name of param to get from instance. * @param defaultValue Value returned when no param is defined in Config.xlsx. */ - public getParam(instance: IGBInstance, name: string, defaultValue?: T, platform=false): any { + public getParam(instance: IGBInstance, name: string, defaultValue?: T, platform = false): any { let value = null; let params; name = name.trim(); @@ -774,8 +784,8 @@ ENDPOINT_UPDATE=true value = null; } - if (!value && platform){ - value = process.env[name.replace(/ /g, "_").toUpperCase()]; + if (!value && platform) { + value = process.env[name.replace(/ /g, '_').toUpperCase()]; } if (value && typeof defaultValue === 'boolean') { @@ -815,19 +825,17 @@ ENDPOINT_UPDATE=true let libraryPath = GBConfigService.get('STORAGE_LIBRARY'); if (!Fs.existsSync(libraryPath)) { - mkdirp.sync(libraryPath); + mkdirp.sync(libraryPath); } await this.syncBotStorage(instances, 'default', deployer, libraryPath); - + const files = Fs.readdirSync(libraryPath); await CollectionUtil.asyncForEach(files, async file => { - - if (file.trim().toLowerCase() !== 'default.gbai'){ + if (file.trim().toLowerCase() !== 'default.gbai') { + let botId = file.replace(/\.gbai/, ''); - let botId = file.replace(/\.gbai/, ''); - - await this.syncBotStorage(instances, botId, deployer, libraryPath); + await this.syncBotStorage(instances, botId, deployer, libraryPath); } }); } @@ -836,9 +844,8 @@ ENDPOINT_UPDATE=true let instance = instances.find(p => p.botId.toLowerCase().trim() === botId.toLowerCase().trim()); if (!instance) { - GBLog.info(`Importing package ${botId}...`); - + // Creates a bot. let mobile = null, @@ -848,12 +855,11 @@ ENDPOINT_UPDATE=true const gbaiPath = Path.join(libraryPath, `${botId}.gbai`); if (!Fs.existsSync(gbaiPath)) { - Fs.mkdirSync(gbaiPath, { recursive: true }); const base = Path.join(process.env.PWD, 'templates', 'default.gbai'); - Fs.cpSync(Path.join(base, `default.gbkb`), Path.join(gbaiPath,`default.gbkb`), { + Fs.cpSync(Path.join(base, `default.gbkb`), Path.join(gbaiPath, `default.gbkb`), { errorOnExist: false, force: true, recursive: true @@ -876,7 +882,7 @@ ENDPOINT_UPDATE=true Fs.cpSync(Path.join(base, `default.gbdialog`), Path.join(gbaiPath, `default.gbdialog`), { errorOnExist: false, force: true, - recursive: true, + recursive: true }); Fs.cpSync(Path.join(base, `default.gbdrive`), Path.join(gbaiPath, `default.gbdrive`), { errorOnExist: false, diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index 9245f8a0..65a42f1e 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -324,10 +324,20 @@ export class GBDeployer implements IGBDeployer { public async loadOrCreateEmptyVectorStore(min: GBMinInstance): Promise { let vectorStore: HNSWLib; - const azureOpenAIKey = await min.core.getParam(min.instance, 'Azure Open AI Key', null); - const azureOpenAIVersion = await min.core.getParam(min.instance, 'Azure Open AI Version', null); - const azureOpenAIApiInstanceName = await min.core.getParam(min.instance, 'Azure Open AI Instance', null); - const azureOpenAIEmbeddingModel = await min.core.getParam(min.instance, 'Azure Open AI Embedding Model', null); + const azureOpenAIKey = await (min.core as any)['getParam'](min.instance, 'Azure Open AI Key', null, true); + const azureOpenAIVersion = await (min.core as any)['getParam'](min.instance, 'Azure Open AI Version', null, true); + const azureOpenAIApiInstanceName = await (min.core as any)['getParam']( + min.instance, + 'Azure Open AI Instance', + null, + true + ); + const azureOpenAIEmbeddingModel = await (min.core as any)['getParam']( + min.instance, + 'Azure Open AI Embedding Model', + null, + true + ); let embedding; if (!azureOpenAIEmbeddingModel) { @@ -663,6 +673,10 @@ export class GBDeployer implements IGBDeployer { // Updates instance object. await this.core.saveInstance(min.instance); + GBLogEx.info(min, `Reloading bot ${min.botId}...`); + 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/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index f6cc73a7..11af269b 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -827,7 +827,7 @@ export class GBMinService { min.sandBoxMap = {}; min['scheduleMap'] = {}; min['conversationWelcomed'] = {}; - if (await min.core.getParam(min.instance, 'Azure Open AI Instance', null)) { + if (await min.core.getParam(min.instance, 'Answer Mode', null)) { const gbkbPath = DialogKeywords.getGBAIPath(min.botId, 'gbkb'); min['vectorStorePath'] = Path.join('work', gbkbPath, 'docs-vectorized'); min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min); diff --git a/packages/kb.gbapp/services/KBService.ts b/packages/kb.gbapp/services/KBService.ts index 34e11b49..a5d9c2b8 100644 --- a/packages/kb.gbapp/services/KBService.ts +++ b/packages/kb.gbapp/services/KBService.ts @@ -54,6 +54,7 @@ import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'; import { Document } from 'langchain/document'; import getColors from 'get-image-colors'; + import { GBDialogStep, GBLog, diff --git a/templates/crawler.gbai/crawler.gbot/config.csv b/templates/crawler.gbai/crawler.gbot/config.csv new file mode 100644 index 00000000..00722edb --- /dev/null +++ b/templates/crawler.gbai/crawler.gbot/config.csv @@ -0,0 +1,3 @@ +name,value +Website,https://pragmatismo.cloud +Answer Mode,direct \ No newline at end of file diff --git a/templates/reminder.gbai/reminder.gbdata/reminders.csv b/templates/reminder.gbai/reminder.gbdata/reminders.csv new file mode 100644 index 00000000..e69de29b diff --git a/templates/reminder.gbai/reminder.gbdialog/add-reminder.bas b/templates/reminder.gbai/reminder.gbdialog/add-reminder.bas new file mode 100644 index 00000000..bd167040 --- /dev/null +++ b/templates/reminder.gbai/reminder.gbdialog/add-reminder.bas @@ -0,0 +1,6 @@ +PARAM when +PARAM subject + +DESCRIPTION Called when someone asks to save a quick meeting. + +SAVE "reminders.csv", when, subject \ No newline at end of file diff --git a/templates/reminder.gbai/reminder.gbdialog/reminder.bas b/templates/reminder.gbai/reminder.gbdialog/reminder.bas new file mode 100644 index 00000000..e3de2516 --- /dev/null +++ b/templates/reminder.gbai/reminder.gbdialog/reminder.bas @@ -0,0 +1,7 @@ +REM SET SCHEDULER "1 * * * * " + +data = FIND "reminder.csv", "when=" + hour + +if (data) THEN + TALK TO admin, data.subject +end if \ No newline at end of file diff --git a/templates/reminder.gbai/reminder.gbdialog/start.bas b/templates/reminder.gbai/reminder.gbdialog/start.bas new file mode 100644 index 00000000..ddbdb886 --- /dev/null +++ b/templates/reminder.gbai/reminder.gbdialog/start.bas @@ -0,0 +1,3 @@ +BEGIN SYSTEM PROMPT + You are a reminder AI assistant. +END SYSTEM PROMPT \ No newline at end of file