fix(llm.gblib): Tool fix.

This commit is contained in:
Rodrigo Rodriguez 2024-09-04 00:18:19 -03:00
parent 66ad2d3ee1
commit 63efa588a8
9 changed files with 92 additions and 47 deletions

1
.vscode/launch.json vendored
View file

@ -14,7 +14,6 @@
"NODE_NO_WARNINGS":"1" "NODE_NO_WARNINGS":"1"
}, },
"args": [ "args": [
"--max-old-space-size 5120",
"--no-deprecation", "--no-deprecation",
"--loader ts-node/esm", "--loader ts-node/esm",
"--require ${workspaceRoot}/suppress-node-warnings.cjs", "--require ${workspaceRoot}/suppress-node-warnings.cjs",

View file

@ -577,11 +577,11 @@ export class GBVMService extends GBService {
// Transfers auto variables into global object. // Transfers auto variables into global object.
for(__indexer in this.variables) { for (const key of Object.keys(this.variables)) {
global[__indexer] = this.variables[__indexer]; global[key] = this.variables[key];
console.log('Defining global variable: ' + key);
} }
// Defines local utility BASIC functions. // Defines local utility BASIC functions.
const ubound = (gbarray) => { const ubound = (gbarray) => {
@ -805,21 +805,22 @@ export class GBVMService extends GBService {
const propertiesExp = propertiesText[i]; const propertiesExp = propertiesText[i];
const t = getType(propertiesExp[2]); const t = getType(propertiesExp[2]);
let element; let element;
const description = propertiesExp[4]?.trim();
if (t === 'enum') { if (t === 'enum') {
const list = propertiesExp[2] as any; const list = propertiesExp[2] as any;
element = z.enum(list.split(',')); element = z.enum(list.split(','));
} else if (t === 'string') { } else if (t === 'string') {
element = z.string(); element = z.string({description:description});
} else if (t === 'object') { } else if (t === 'object') {
element = z.string(); // Assuming 'object' is represented as a string here element = z.string({description:description}); // Assuming 'object' is represented as a string here
} else if (t === 'number') { } else if (t === 'number') {
element = z.number(); element = z.number({description:description});
} else { } else {
GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`); GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`);
} }
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;
} }
@ -829,7 +830,7 @@ export class GBVMService extends GBService {
function: { function: {
name: mainName, name: mainName,
description: description ? description : '', description: description ? description : '',
parameters: zodToJsonSchema(z.object(properties)) schema: zodToJsonSchema(z.object(properties))
}, },
arguments: propertiesText.reduce((acc, prop) => { arguments: propertiesText.reduce((acc, prop) => {
acc[prop[1].trim()] = prop[3]?.trim(); // Assuming value is in the 3rd index acc[prop[1].trim()] = prop[3]?.trim(); // Assuming value is in the 3rd index
@ -1049,7 +1050,7 @@ export class GBVMService extends GBService {
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
let variables = []; let variables = {};
// These variables will be automatically be available as normal BASIC variables. // These variables will be automatically be available as normal BASIC variables.
@ -1090,7 +1091,7 @@ export class GBVMService extends GBService {
const botId = min.botId; const botId = min.botId;
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`); const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
const gbdialogPath = urlJoin(process.cwd(), 'work', path); const gbdialogPath = urlJoin(process.cwd(), 'work', path);
const scriptPath = urlJoin(gbdialogPath, `${text}.js`); const scriptFilePath = urlJoin(gbdialogPath, `${text}.js`);
let code = min.sandBoxMap[text]; let code = min.sandBoxMap[text];
const channel = step ? step.context.activity.channelId : 'web'; const channel = step ? step.context.activity.channelId : 'web';
@ -1148,7 +1149,7 @@ export class GBVMService extends GBService {
context: 'sandbox' context: 'sandbox'
} }
}); });
const s = new VMScript(code, { filename: scriptPath }); const s = new VMScript(code, { filename: scriptFilePath });
result = vm1.run(s); result = vm1.run(s);
}); });
})(); })();
@ -1167,16 +1168,17 @@ export class GBVMService extends GBService {
min: 0, min: 0,
max: 0, max: 0,
debug: debug, debug: debug,
debuggerport: GBVMService.DEBUGGER_PORT, // debuggerport: GBVMService.DEBUGGER_PORT,
botId: botId, botId: botId,
cpu: 100, cpu: 100,
memory: 50000, memory: 50000,
time: 60 * 60 * 24 * 14, time: 60 * 60 * 24 * 14,
cwd: scriptPath, cwd: gbdialogPath,
script: runnerPath script: runnerPath
}); });
result = await run(code, { filename: scriptPath, sandbox: sandbox }); result = await run(code, Object.assign( sandbox, { filename: scriptFilePath}));
} }
} catch (error) { } catch (error) {
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`); throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);

View file

@ -118,7 +118,6 @@ const systemVariables = [
'valueOf' 'valueOf'
]; ];
export const createVm2Pool = ({ min, max, ...limits }) => { export const createVm2Pool = ({ min, max, ...limits }) => {
limits = Object.assign( limits = Object.assign(
{ {
@ -140,8 +139,14 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
let stderrCache = ''; let stderrCache = '';
const run = async (code: any, scope: any) => { const run = async (code: any, scope: any) => {
// Configure environment variables
const env = Object.assign({}, process.env, {
NODE_ENV: 'production',
NODE_OPTIONS: '' // Clear NODE_OPTIONS if needed
});
const childProcess = spawn( const childProcess = spawn(
'cpulimit', '/usr/bin/cpulimit',
[ [
'-ql', '-ql',
limits.cpu, limits.cpu,
@ -153,7 +158,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
limits.script, limits.script,
ref ref
], ],
{ cwd: limits.cwd, shell: false } { cwd: limits.cwd, shell: true, env: env }
); );
childProcess.stdout.on('data', data => { childProcess.stdout.on('data', data => {
@ -186,7 +191,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
// Only attach if called by debugger/run. // Only attach if called by debugger/run.
if (GBServer.globals.debuggers[limits.botId]) { if (limits.debug) {
const debug = async () => { const debug = async () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
CDP(async client => { CDP(async client => {
@ -212,7 +217,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
}); });
} }
GBServer.globals.debuggers[limits.botId].scope = variablesText; GBServer.globals.debuggers[limits.botId].scope = variablesText;
GBLogEx.info(min,`Breakpoint variables: ${variablesText}`); // (zero-based) GBLogEx.info(min, `Breakpoint variables: ${variablesText}`); // (zero-based)
// Processes breakpoint hits. // Processes breakpoint hits.
if (hitBreakpoints.length >= 1) { if (hitBreakpoints.length >= 1) {

View file

@ -26,10 +26,13 @@ const server = net1.createServer(socket => {
const buffer = []; const buffer = [];
const sync = async () => { const sync = async () => {
const request = buffer.join('').toString(); const request = buffer.join('').toString();
console.log(request);
if (request.includes('\n')) { if (request.includes('\n')) {
try { try {
const { code, scope } = JSON.parse(request); const { code, scope } = JSON.parse(request);
const result = await evaluate(code, { const result = await evaluate(code, {
...scope, ...scope,
module: null module: null
@ -45,6 +48,11 @@ const server = net1.createServer(socket => {
} }
} }
}; };
socket.on('error', err => {
console.log(err);
});
socket.on('data', data => { socket.on('data', data => {
buffer.push(data); buffer.push(data);

View file

@ -39,6 +39,7 @@ import { ChatGeneration, Generation } from '@langchain/core/outputs';
import { import {
AIMessagePromptTemplate, AIMessagePromptTemplate,
ChatPromptTemplate, ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate, HumanMessagePromptTemplate,
MessagesPlaceholder MessagesPlaceholder
} from '@langchain/core/prompts'; } from '@langchain/core/prompts';
@ -72,6 +73,7 @@ import {
} from 'langchain/chains/sql_db'; } from 'langchain/chains/sql_db';
import { GBUtil } from '../../../src/util.js'; import { GBUtil } from '../../../src/util.js';
import { z } from 'zod'; import { z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
export interface CustomOutputParserFields {} export interface CustomOutputParserFields {}
export type ExpectedOutput = any; export type ExpectedOutput = any;
@ -323,25 +325,38 @@ 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);
let openaiTools = tools.map(tool => convertToOpenAITool(tool, { strict: true }));
function updateFields(schemas) {
schemas.forEach(schema => {
if (schema.function && schema.function.parameters) {
delete schema.function.strict;
schema.function.parameters.additionalProperties = false;
}
});
}
updateFields(openaiTools);
const modelWithTools = model.bind({ const modelWithTools = model.bind({
tools: tools.map(convertToOpenAITool) tools: openaiTools
}); });
const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([ const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([
AIMessagePromptTemplate.fromTemplate( SystemMessagePromptTemplate.fromTemplate(
` `
Answer the question without calling any tool, but if there is a need to call: ${systemPrompt}
You have access to the following set of tools.
Here are the names and descriptions for each tool:
${toolsAsText} When a tool is required, use the tools provided below.
The tools available to you are listed below, along with their names, parameters, and descriptions:
IMPORTANT: Never call a tool with a missing required param, without asking them first to the user!
List of tools:
${toolsAsText}
Do not use any previous tools output in the {chat_history}.
` `
), ),
new MessagesPlaceholder('chat_history'), new MessagesPlaceholder('chat_history'),
AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question} HumanMessagePromptTemplate.fromTemplate(`Follow Up Input: {question}
Standalone question:`) Standalone question:`)
]); ]);
@ -353,14 +368,23 @@ export class ChatServices {
]); ]);
const toolsResultPrompt = ChatPromptTemplate.fromMessages([ const toolsResultPrompt = ChatPromptTemplate.fromMessages([
AIMessagePromptTemplate.fromTemplate( SystemMessagePromptTemplate.fromTemplate(
`The tool just returned value in last call. Using {chat_history} `
rephrase the answer to the user using this tool output. ${systemPrompt}
List of tools:
${toolsAsText}
` `
), ),
new MessagesPlaceholder('chat_history'), AIMessagePromptTemplate.fromTemplate(
AIMessagePromptTemplate.fromTemplate(`Tool output: {tool_output} `
Standalone question:`) The tool just returned value in last call answer the question based on tool description.
`
),
HumanMessagePromptTemplate.fromTemplate(`Tool output: {tool_output}
Folowing answer:`)
]); ]);
const jsonInformation = `VERY IMPORTANT: ALWAYS return VALID standard JSON with the folowing structure: 'text' as answer, const jsonInformation = `VERY IMPORTANT: ALWAYS return VALID standard JSON with the folowing structure: 'text' as answer,
@ -415,13 +439,14 @@ export class ChatServices {
tool_output: async (output: object) => { tool_output: async (output: object) => {
const name = output['func'][0].function.name; const name = output['func'][0].function.name;
const args = JSON.parse(output['func'][0].function.arguments); const args = JSON.parse(output['func'][0].function.arguments);
GBLogEx.info(min, `Running .gbdialog '${name}' as LLM tool...`); GBLogEx.info(min, `LLM Tool called .gbdialog '${name}'...`);
const pid = GBVMService.createProcessInfo(null, min, 'LLM', null); const pid = GBVMService.createProcessInfo(null, min, 'LLM', null);
return await GBVMService.callVM(name, min, false, pid, false, args); return await GBVMService.callVM(name, min, false, pid, false, args);
}, },
chat_history: async () => { chat_history: async () => {
const { chat_history } = await memory.loadMemoryVariables({}); const { chat_history } = await memory.loadMemoryVariables({});
return chat_history; return chat_history;
} }
}, },
@ -623,10 +648,10 @@ export class ChatServices {
return Object.keys(tools) return Object.keys(tools)
.map(toolname => { .map(toolname => {
const tool = tools[toolname]; const tool = tools[toolname];
const properties = tool.lc_kwargs.parameters.properties; const properties = tool.lc_kwargs.schema.properties;
const params = Object.keys(properties).map(param => { const params = Object.keys(properties).map(param => {
const { description, type } = properties[param]; const { description, type } = properties[param];
return `${param} (${type}): ${description}`; return `${param} *REQUIRED* (${type}): ${description}`;
}).join(', '); }).join(', ');
return `- ${tool.name}: ${tool.description}\n Parameters: ${params?? 'No parameters'}`; return `- ${tool.name}: ${tool.description}\n Parameters: ${params?? 'No parameters'}`;
@ -649,7 +674,7 @@ export class ChatServices {
if (funcObj) { if (funcObj) {
// TODO: Use ajv. // TODO: Use ajv.
funcObj.schema = jsonSchemaToZod(funcObj.parameters); funcObj.schema = eval(funcObj.schema);
functions.push(new DynamicStructuredTool(funcObj)); functions.push(new DynamicStructuredTool(funcObj));
} }
} }

View file

@ -1,5 +1,6 @@
name,price name,price
fax, 500 fax, 500
TV, 1200 tv, 1200
mobile,200 mobile,200
console, 250 console, 250
chocolate, 30

1 name price
2 fax 500
3 TV tv 1200
4 mobile 200
5 console 250
6 chocolate 30

View file

@ -1,6 +1,9 @@
PARAM product AS string LIKE telephone DESCRIPTION The name of the product to have the price retrieved. PARAM product AS string LIKE fax DESCRIPTION "Required name of the item you want to inquire about."
DESCRIPTION Returns the price of the given product. DESCRIPTION "Returns the price of the specified product name."
product = FIND "products.csv", "name = ${product}" price = -1
price = product.price productRecord = FIND "products.csv", "name = ${product}"
IF (productRecord) THEN
price = productRecord.price
END IF
RETURN price RETURN price

View file

@ -4,5 +4,6 @@ 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.
If get price tool return value of -1, says there is no such product.
END SYSTEM PROMPT END SYSTEM PROMPT

View file

@ -1,2 +1,3 @@
name,value name,value
Answer Mode,tool Answer Mode,tool
Start Dialog,start
1 name value
2 Answer Mode tool
3 Start Dialog start