fix(llm.gblib): Tool fix. More templates.

This commit is contained in:
Rodrigo Rodriguez 2024-09-02 20:16:56 -03:00
parent 6aaae55a61
commit 6d814c0c1d
13 changed files with 122 additions and 105 deletions

View file

@ -168,34 +168,27 @@ export class GBAdminService implements IGBAdminService {
) { ) {
const packageName = text.split(' ')[1]; const packageName = text.split(' ')[1];
if (!this.isSharePointPath(packageName)) { const folderName = text.split(' ')[2];
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); const packageType = Path.extname(folderName).substr(1);
if (additionalPath === undefined) { const gbaiPath = DialogKeywords.getGBAIPath(min.instance.botId, packageType, null);
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.'); const localFolder = Path.join('work', gbaiPath);
}
await deployer['deployPackage2'](min, user, urlJoin(additionalPath, packageName));
} else {
const folderName = text.split(' ')[2];
const packageType = Path.extname(folderName).substr(1);
const gbaiPath = DialogKeywords.getGBAIPath(min.instance.botId, packageType, null);
const localFolder = Path.join('work', gbaiPath);
// .gbot packages are handled using storage API, so no download // .gbot packages are handled using storage API, so no download
// of local resources is required. // of local resources is required.
const gbai = DialogKeywords.getGBAIPath(min.instance.botId); const gbai = DialogKeywords.getGBAIPath(min.instance.botId);
if (packageType === 'gbkb') { if (packageType === 'gbkb') {
await deployer['cleanupPackage'](min.instance, packageName); await deployer['cleanupPackage'](min.instance, packageName);
}
if (!GBConfigService.get('STORAGE_NAME')) {
const path = Path.join(GBConfigService.get('STORAGE_LIBRARY'), gbaiPath);
GBUtil.copyIfNewerRecursive(path, localFolder);
} else {
await deployer['downloadFolder'](min, Path.join('work', `${gbai}`), Path.basename(localFolder));
}
await deployer['deployPackage2'](min, user, localFolder);
} }
if (!GBConfigService.get('STORAGE_NAME')) {
const path = Path.join(GBConfigService.get('STORAGE_LIBRARY'), gbaiPath);
GBUtil.copyIfNewerRecursive(path, localFolder);
} else {
await deployer['downloadFolder'](min, Path.join('work', `${gbai}`), Path.basename(localFolder));
}
await deployer['deployPackage2'](min, user, localFolder);
} }
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) { public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
const service = await AzureDeployerService.createInstance(deployer); const service = await AzureDeployerService.createInstance(deployer);

View file

@ -986,7 +986,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
appSettings: [ appSettings: [
{ name: 'WEBSITES_CONTAINER_START_TIME_LIMIT', value: `${WebSiteResponseTimeout}` }, { name: 'WEBSITES_CONTAINER_START_TIME_LIMIT', value: `${WebSiteResponseTimeout}` },
{ name: 'WEBSITE_NODE_DEFAULT_VERSION', value: GBAdminService.getNodeVersion() }, { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: GBAdminService.getNodeVersion() },
{ name: 'ADDITIONAL_DEPLOY_PATH', value: `` },
{ name: 'ADMIN_PASS', value: `${instance.adminPass}` }, { name: 'ADMIN_PASS', value: `${instance.adminPass}` },
{ name: 'BOT_ID', value: `${instance.botId}` }, { name: 'BOT_ID', value: `${instance.botId}` },
{ name: 'CLOUD_SUBSCRIPTIONID', value: `${instance.cloudSubscriptionId}` }, { name: 'CLOUD_SUBSCRIPTIONID', value: `${instance.cloudSubscriptionId}` },

View file

@ -453,7 +453,8 @@ export class GBVMService extends GBService {
// Converts General Bots BASIC into regular VBS // Converts General Bots BASIC into regular VBS
let basicCode: string = Fs.readFileSync(filename, 'utf8'); let basicCode: string = Fs.readFileSync(filename, 'utf8');
basicCode = GBVMService.normalizeQuotes(basicCode);
// Pre process SET SCHEDULE calls. // Pre process SET SCHEDULE calls.
const schedules = GBVMService.getSetScheduleKeywordArgs(basicCode); const schedules = GBVMService.getSetScheduleKeywordArgs(basicCode);
@ -764,9 +765,6 @@ export class GBVMService extends GBService {
} }
} }
if (text) {
text = GBVMService.normalizeQuotes(text);
}
resolve(text); resolve(text);
}); });
}); });
@ -783,14 +781,15 @@ export class GBVMService extends GBService {
return text; return text;
} }
public static getMetadata(mainName: string, propertiesText, description) { public static getMetadata(mainName: string, propertiesText: string[][], description: string) {
let properties = {}; let properties = {};
if (!propertiesText || !description) { if (!propertiesText || !description) {
return {}; return {};
} }
const getType = asClause => {
const getType = (asClause: string) => {
asClause = asClause.trim().toUpperCase(); asClause = asClause.trim().toUpperCase();
if (asClause.indexOf('STRING') !== -1) { if (asClause.indexOf('STRING') !== -1) {
return 'string'; return 'string';
} else if (asClause.indexOf('OBJECT') !== -1) { } else if (asClause.indexOf('OBJECT') !== -1) {
@ -801,41 +800,46 @@ export class GBVMService extends GBService {
return 'enum'; return 'enum';
} }
}; };
for (let i = 0; i < propertiesText.length; i++) { for (let i = 0; i < propertiesText.length; i++) {
const propertiesExp = propertiesText[i]; const propertiesExp = propertiesText[i];
const t = getType(propertiesExp[2]); const t = getType(propertiesExp[2]);
let element; let element;
if (t === 'enum') { if (t === 'enum') {
element = z.enum(propertiesExp[2].split(',')); const list = propertiesExp[2] as any;
element = z.enum(list.split(','));
} else if (t === 'string') { } else if (t === 'string') {
element = z.string(); element = z.string();
} else if (t === 'object') { } else if (t === 'object') {
element = z.string(); element = z.string(); // Assuming 'object' is represented as a string here
} else if (t === 'number') { } else if (t === 'number') {
element = z.number(); element = z.number();
} else { } else {
GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`); GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`);
} }
element.describe(propertiesExp[3]); element['description'] = propertiesExp[4]?.trim(); // Assuming description is in the 4th index
element['type'] = t; element['type'] = t;
properties[propertiesExp[1].trim()] = element; properties[propertiesExp[1].trim()] = element;
} }
let json = { const json = {
type: 'function', type: 'function',
function: { function: {
name: `${mainName}`, name: mainName,
description: description ? description : '', description: description ? description : '',
parameters: zodToJsonSchema(z.object(properties)) parameters: zodToJsonSchema(z.object(properties))
} },
arguments: propertiesText.reduce((acc, prop) => {
acc[prop[1].trim()] = prop[3]?.trim(); // Assuming value is in the 3rd index
return acc;
}, {})
}; };
return json; return json;
} }
public async parseField(line) { public async parseField(line) {
let required = line.indexOf('*') !== -1; let required = line.indexOf('*') !== -1;
let unique = /\bunique\b/gi.test(line); let unique = /\bunique\b/gi.test(line);
@ -913,7 +917,7 @@ export class GBVMService extends GBService {
// Pre-process "off-line" static KEYWORDS. // Pre-process "off-line" static KEYWORDS.
let emmit = true; let emmit = true;
const params = /^\s*PARAM\s*(.*)\s*AS\s*(.*)\s*LIKE\s*(.*)/gim; const params = /^\s*PARAM\s*(.*)\s*AS\s*(.*)\s*LIKE\s*(.*)\s*DESCRIPTION\s*(.*)/gim;
const param = params.exec(line); const param = params.exec(line);
if (param) { if (param) {
properties.push(param); properties.push(param);

View file

@ -48,7 +48,7 @@ import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import urlJoin from 'url-join'; import urlJoin from 'url-join';
import Excel from 'exceljs'; import Excel from 'exceljs';
import { BufferWindowMemory } from 'langchain/memory'; import { BufferWindowMemory } from 'langchain/memory';
import { TwitterApi } from 'twitter-api-v2'; import csvdb from 'csv-database';
import Path from 'path'; import Path from 'path';
import ComputerVisionClient from '@azure/cognitiveservices-computervision'; import ComputerVisionClient from '@azure/cognitiveservices-computervision';
import ApiKeyCredentials from '@azure/ms-rest-js'; import ApiKeyCredentials from '@azure/ms-rest-js';
@ -345,19 +345,22 @@ export class SystemKeywords {
const memoryBeforeGC = process.memoryUsage().heapUsed / 1024 / 1024; // in MB const memoryBeforeGC = process.memoryUsage().heapUsed / 1024 / 1024; // in MB
delete this.cachedMerge[pid]; delete this.cachedMerge[pid];
// Capture memory usage before GC // Capture memory usage before GC
GBLogEx.info(min, ``); GBLogEx.info(min, ``);
setFlagsFromString('--expose_gc'); setFlagsFromString('--expose_gc');
const gc = runInNewContext('gc'); // nocommit const gc = runInNewContext('gc'); // nocommit
gc(); gc();
// Capture memory usage after GC // Capture memory usage after GC
const memoryAfterGC = process.memoryUsage().heapUsed / 1024 / 1024; // in MB const memoryAfterGC = process.memoryUsage().heapUsed / 1024 / 1024; // in MB
GBLogEx.info(min, `BASIC: Closing Handles... From ${memoryBeforeGC.toFixed(2)} MB to ${memoryAfterGC.toFixed(2)} MB`); GBLogEx.info(
min,
`BASIC: Closing Handles... From ${memoryBeforeGC.toFixed(2)} MB to ${memoryAfterGC.toFixed(2)} MB`
);
} }
public async asPDF({ pid, data }) { public async asPDF({ pid, data }) {
let file = await this.renderTable(pid, data, true, false); let file = await this.renderTable(pid, data, true, false);
return file; return file;
@ -746,7 +749,7 @@ export class SystemKeywords {
*/ */
public async saveToStorageBatch({ pid, table, rows }): Promise<void> { public async saveToStorageBatch({ pid, table, rows }): Promise<void> {
const { min } = await DialogKeywords.getProcessInfo(pid); const { min } = await DialogKeywords.getProcessInfo(pid);
if (rows.length === 0) { if (rows.length === 0) {
return; return;
} }
@ -755,7 +758,6 @@ export class SystemKeywords {
let rowsDest = []; let rowsDest = [];
rows.forEach(row => { rows.forEach(row => {
if (GBUtil.hasSubObject(row)) { if (GBUtil.hasSubObject(row)) {
row = this.flattenJSON(row); row = this.flattenJSON(row);
} }
@ -772,7 +774,7 @@ export class SystemKeywords {
row = null; row = null;
}); });
GBLogEx.info(min, `SAVE '${table}': ${rows.length} row(s).`); GBLogEx.info(min, `SAVE '${table}': ${rows.length} row(s).`);
await retry( await retry(
async bail => { async bail => {
await t.bulkCreate(rowsDest); await t.bulkCreate(rowsDest);
@ -1039,7 +1041,7 @@ export class SystemKeywords {
public static async getFilter(text) { public static async getFilter(text) {
let filter; let filter;
const operators = [/\<\=/, /\<\>/, /\>\=/, /\</, /\>/, /\bnot in\b/, /\bin\b/, /\=/]; const operators = [/\<\=/, /\<\>/, /\>\=/, /\</, /\>/,/\blike\b/, /\bnot in\b/, /\bin\b/, /\=/];
let done = false; let done = false;
await CollectionUtil.asyncForEach(operators, async op => { await CollectionUtil.asyncForEach(operators, async op => {
var re = new RegExp(op, 'gi'); var re = new RegExp(op, 'gi');
@ -1193,6 +1195,23 @@ export class SystemKeywords {
header = results.text[0]; header = results.text[0];
rows = results.text; rows = results.text;
} else if (file.indexOf('.csv') !== -1) {
let res;
let path = DialogKeywords.getGBAIPath(min.botId, `gbdata`);
const csvFile = Path.join(GBConfigService.get('STORAGE_LIBRARY'), path, file);
const firstLine = Fs.readFileSync(csvFile, 'utf8').split('\n')[0];
const headers = firstLine.split(',');
const db = await csvdb(csvFile, headers, ',');
if (args[0]) {
const systemFilter = await SystemKeywords.getFilter(args[0]);
let filter = {};
filter[systemFilter.columnName] = systemFilter.value;
res = await db.get(filter);
} else {
res = await db.get();
}
return res.length > 1 ? res : res[0];
} else { } else {
const t = this.getTableFromName(file, min); const t = this.getTableFromName(file, min);
@ -1745,27 +1764,27 @@ export class SystemKeywords {
private flattenJSON(obj, res = {}, separator = '_', parent = null) { private flattenJSON(obj, res = {}, separator = '_', parent = null) {
for (let key in obj) { for (let key in obj) {
if (!obj.hasOwnProperty(key) || typeof obj[key] === 'function') { if (!obj.hasOwnProperty(key) || typeof obj[key] === 'function') {
continue; continue;
} }
if (typeof obj[key] !== 'object' || obj[key] instanceof Date) { if (typeof obj[key] !== 'object' || obj[key] instanceof Date) {
// If not defined already, add the flattened field. // If not defined already, add the flattened field.
const newKey = `${parent ? parent + separator : ''}${key}`; const newKey = `${parent ? parent + separator : ''}${key}`;
if (!res.hasOwnProperty(newKey)) { if (!res.hasOwnProperty(newKey)) {
res[newKey] = obj[key]; res[newKey] = obj[key];
} else {
GBLog.verbose(`Ignoring duplicated field in flatten operation to storage: ${key}.`);
}
} else { } else {
// Create a temporary reference to the nested object to prevent memory leaks. GBLog.verbose(`Ignoring duplicated field in flatten operation to storage: ${key}.`);
const tempObj = obj[key];
this.flattenJSON(tempObj, res, separator, `${parent ? parent + separator : ''}${key}`);
// Clear the reference to avoid holding unnecessary objects in memory.
obj[key] = null;
} }
} else {
// Create a temporary reference to the nested object to prevent memory leaks.
const tempObj = obj[key];
this.flattenJSON(tempObj, res, separator, `${parent ? parent + separator : ''}${key}`);
// Clear the reference to avoid holding unnecessary objects in memory.
obj[key] = null;
}
} }
return res; return res;
} }
public async getCustomToken({ pid, tokenName }) { public async getCustomToken({ pid, tokenName }) {
const { min } = await DialogKeywords.getProcessInfo(pid); const { min } = await DialogKeywords.getProcessInfo(pid);
@ -2349,7 +2368,7 @@ export class SystemKeywords {
valueFound = found[e]; valueFound = found[e];
} }
}); });
const equals = const equals =
typeof value === 'string' && typeof valueFound === 'string' typeof value === 'string' && typeof valueFound === 'string'
? value?.toLowerCase() != valueFound?.toLowerCase() ? value?.toLowerCase() != valueFound?.toLowerCase()
@ -2446,31 +2465,24 @@ export class SystemKeywords {
} }
/** /**
* Publishs a tweet to X. * Publishs a post to BlueSky .
* *
* TWEET "My tweet text" * BlueSky "My BlueSky text"
*/ */
public async tweet({ pid, text }) { public async postToBlueSky({ pid, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
const consumer_key = min.core.getParam(min.instance, 'Twitter Consumer Key', null); const consumer_key = min.core.getParam(min.instance, 'BlueSky Consumer Key', null);
const consumer_secret = min.core.getParam(min.instance, 'Twitter Consumer Key Secret', null); const consumer_secret = min.core.getParam(min.instance, 'BlueSky Consumer Key Secret', null);
const access_token_key = min.core.getParam(min.instance, 'Twitter Access Token', null); const access_token_key = min.core.getParam(min.instance, 'BlueSky Access Token', null);
const access_token_secret = min.core.getParam(min.instance, 'Twitter Access Token Secret', null); const access_token_secret = min.core.getParam(min.instance, 'BlueSky Access Token Secret', null);
if (!consumer_key || !consumer_secret || !access_token_key || !access_token_secret) { if (!consumer_key || !consumer_secret || !access_token_key || !access_token_secret) {
GBLogEx.info(min, 'Twitter not configured in .gbot.'); GBLogEx.info(min, 'BlueSky not configured in .gbot.');
} }
throw new Error('Not implemented yet.');
const client = new TwitterApi({ GBLogEx.info(min, `BlueSky Automation: ${text}.`);
appKey: consumer_key,
appSecret: consumer_secret,
accessToken: access_token_key,
accessSecret: access_token_secret
});
await client.v2.tweet(text);
GBLogEx.info(min, `Twitter Automation: ${text}.`);
} }
/** /**

View file

@ -424,10 +424,12 @@ export class GBMinService {
GBServer.globals.server GBServer.globals.server
.all(`/${min.instance.botId}/whatsapp`, async (req, res) => { .all(`/${min.instance.botId}/whatsapp`, async (req, res) => {
if (req.query['hub.mode'] === 'subscribe') { if (req.query['hub.mode'] === 'subscribe') {
const val = req.query['hub.verify_token']; const val = req.query['hub.verify_token'];
const challenge = min.core.getParam<string>(min.instance, `Meta Challenge`, null);
if (val === process.env.META_CHALLENGE) { if (challenge && val === challenge) {
res.send(req.query['hub.challenge']); res.send(req.query['hub.challenge']);
res.status(200); res.status(200);
GBLogEx.info(min, `Meta callback OK. ${JSON.stringify(req.query)}`); GBLogEx.info(min, `Meta callback OK. ${JSON.stringify(req.query)}`);

View file

@ -621,8 +621,17 @@ export class ChatServices {
private static getToolsAsText(tools) { private static getToolsAsText(tools) {
return Object.keys(tools) return Object.keys(tools)
.map(toolname => `- ${tools[toolname].name}: ${tools[toolname].description}`) .map(toolname => {
.join('\n'); const tool = tools[toolname];
const properties = tool.lc_kwargs.parameters.properties;
const params = Object.keys(properties).map(param => {
const { description, type } = properties[param];
return `${param} (${type}): ${description}`;
}).join(', ');
return `- ${tool.name}: ${tool.description}\n Parameters: ${params?? 'No parameters'}`;
})
.join('\n');
} }
private static async getTools(min: GBMinInstance) { private static async getTools(min: GBMinInstance) {

View file

@ -1,7 +1,5 @@
name,price name,price
Product A, 230 fax, 500
Product B, 120 TV, 1200
Product C, 180 mobile,200
Product D, 250 console, 250
Product E, 200
Product F, 150

1 name price
2 Product A fax 230 500
3 Product B TV 120 1200
4 Product C mobile 180 200
5 Product D console 250
Product E 200
Product F 150

View file

@ -1,6 +1,6 @@
PARAM product AS string LIKE "Product A" PARAM product AS string LIKE telephone DESCRIPTION The name of the product to have the price retrieved.
DESCRIPTION "Returns the price of the given product." DESCRIPTION Returns the price of the given product.
product = FIND "products.csv", "name LIKE ${product}" product = FIND "products.csv", "name = ${product}"
price = product.price price = product.price
RETURN price RETURN price

View file

@ -4,6 +4,5 @@ There exist some helpful predefined internal tools which can help me by
extending my functionalities or get me helpful information. extending my functionalities or get me helpful information.
These tools **should** be abstracted away from the user. These tools **should** be abstracted away from the user.
These tools can be invoked only by me before I respond to a user. These tools can be invoked only by me before I respond to a user.
Here is the list of my internal tools:
END SYSTEM PROMPT END SYSTEM PROMPT

View file

@ -0,0 +1 @@
Meta Challenge,
1 Meta Challenge