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"
},
"args": [
"--max-old-space-size 5120",
"--no-deprecation",
"--loader ts-node/esm",
"--require ${workspaceRoot}/suppress-node-warnings.cjs",

View file

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

View file

@ -118,7 +118,6 @@ const systemVariables = [
'valueOf'
];
export const createVm2Pool = ({ min, max, ...limits }) => {
limits = Object.assign(
{
@ -140,8 +139,14 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
let stderrCache = '';
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(
'cpulimit',
'/usr/bin/cpulimit',
[
'-ql',
limits.cpu,
@ -153,7 +158,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
limits.script,
ref
],
{ cwd: limits.cwd, shell: false }
{ cwd: limits.cwd, shell: true, env: env }
);
childProcess.stdout.on('data', data => {
@ -186,7 +191,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
// Only attach if called by debugger/run.
if (GBServer.globals.debuggers[limits.botId]) {
if (limits.debug) {
const debug = async () => {
return new Promise((resolve, reject) => {
CDP(async client => {
@ -212,7 +217,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
});
}
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.
if (hitBreakpoints.length >= 1) {

View file

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

View file

@ -39,6 +39,7 @@ import { ChatGeneration, Generation } from '@langchain/core/outputs';
import {
AIMessagePromptTemplate,
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
MessagesPlaceholder
} from '@langchain/core/prompts';
@ -72,6 +73,7 @@ import {
} from 'langchain/chains/sql_db';
import { GBUtil } from '../../../src/util.js';
import { z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
export interface CustomOutputParserFields {}
export type ExpectedOutput = any;
@ -323,25 +325,38 @@ export class ChatServices {
let tools = await ChatServices.getTools(min);
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({
tools: tools.map(convertToOpenAITool)
tools: openaiTools
});
const questionGeneratorTemplate = ChatPromptTemplate.fromMessages([
AIMessagePromptTemplate.fromTemplate(
SystemMessagePromptTemplate.fromTemplate(
`
Answer the question without calling any tool, but if there is a need to call:
You have access to the following set of tools.
Here are the names and descriptions for each tool:
${systemPrompt}
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}
${toolsAsText}
Do not use any previous tools output in the {chat_history}.
`
),
new MessagesPlaceholder('chat_history'),
AIMessagePromptTemplate.fromTemplate(`Follow Up Input: {question}
new MessagesPlaceholder('chat_history'),
HumanMessagePromptTemplate.fromTemplate(`Follow Up Input: {question}
Standalone question:`)
]);
@ -353,14 +368,23 @@ export class ChatServices {
]);
const toolsResultPrompt = ChatPromptTemplate.fromMessages([
AIMessagePromptTemplate.fromTemplate(
`The tool just returned value in last call. Using {chat_history}
rephrase the answer to the user using this tool output.
SystemMessagePromptTemplate.fromTemplate(
`
${systemPrompt}
List of tools:
${toolsAsText}
`
),
new MessagesPlaceholder('chat_history'),
AIMessagePromptTemplate.fromTemplate(`Tool output: {tool_output}
Standalone question:`)
AIMessagePromptTemplate.fromTemplate(
`
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,
@ -415,13 +439,14 @@ export class ChatServices {
tool_output: async (output: object) => {
const name = output['func'][0].function.name;
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);
return await GBVMService.callVM(name, min, false, pid, false, args);
},
chat_history: async () => {
const { chat_history } = await memory.loadMemoryVariables({});
return chat_history;
}
},
@ -623,10 +648,10 @@ export class ChatServices {
return Object.keys(tools)
.map(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 { description, type } = properties[param];
return `${param} (${type}): ${description}`;
return `${param} *REQUIRED* (${type}): ${description}`;
}).join(', ');
return `- ${tool.name}: ${tool.description}\n Parameters: ${params?? 'No parameters'}`;
@ -649,7 +674,7 @@ export class ChatServices {
if (funcObj) {
// TODO: Use ajv.
funcObj.schema = jsonSchemaToZod(funcObj.parameters);
funcObj.schema = eval(funcObj.schema);
functions.push(new DynamicStructuredTool(funcObj));
}
}

View file

@ -1,5 +1,6 @@
name,price
fax, 500
TV, 1200
tv, 1200
mobile,200
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.
DESCRIPTION Returns the price of the given product.
PARAM product AS string LIKE fax DESCRIPTION "Required name of the item you want to inquire about."
DESCRIPTION "Returns the price of the specified product name."
product = FIND "products.csv", "name = ${product}"
price = product.price
RETURN price
price = -1
productRecord = FIND "products.csv", "name = ${product}"
IF (productRecord) THEN
price = productRecord.price
END IF
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.
These tools **should** be abstracted away from the 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

View file

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