From 90df61d2be369192523e2522a804cb25f1363b7b Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Fri, 1 Nov 2024 01:08:30 +0800 Subject: [PATCH 01/15] Sort docs by relevance to !newAction("task") --- src/agent/library/index.js | 19 ++++++------ src/agent/prompter.js | 62 +++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/agent/library/index.js b/src/agent/library/index.js index 677dc113..ae864b03 100644 --- a/src/agent/library/index.js +++ b/src/agent/library/index.js @@ -3,20 +3,21 @@ import * as world from './world.js'; export function docHelper(functions, module_name) { - let docstring = ''; + let docArray = []; for (let skillFunc of functions) { let str = skillFunc.toString(); - if (str.includes('/**')){ - docstring += module_name+'.'+skillFunc.name; - docstring += str.substring(str.indexOf('/**')+3, str.indexOf('**/')) + '\n'; + if (str.includes('/**')) { + let docEntry = `${module_name}.${skillFunc.name}\n`; + docEntry += str.substring(str.indexOf('/**') + 3, str.indexOf('**/')).trim(); + docArray.push(docEntry); } } - return docstring; + return docArray; } export function getSkillDocs() { - let docstring = "\n*SKILL DOCS\nThese skills are javascript functions that can be called when writing actions and skills.\n"; - docstring += docHelper(Object.values(skills), 'skills'); - docstring += docHelper(Object.values(world), 'world'); - return docstring + '*\n'; + let docArray = []; + docArray = docArray.concat(docHelper(Object.values(skills), 'skills')); + docArray = docArray.concat(docHelper(Object.values(world), 'world')); + return docArray; } diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 114064a1..3ba51dd4 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -1,17 +1,17 @@ -import { readFileSync, mkdirSync, writeFileSync} from 'fs'; -import { Examples } from '../utils/examples.js'; -import { getCommandDocs } from './commands/index.js'; -import { getSkillDocs } from './library/index.js'; -import { stringifyTurns } from '../utils/text.js'; -import { getCommand } from './commands/index.js'; - -import { Gemini } from '../models/gemini.js'; -import { GPT } from '../models/gpt.js'; -import { Claude } from '../models/claude.js'; -import { ReplicateAPI } from '../models/replicate.js'; -import { Local } from '../models/local.js'; -import { GroqCloudAPI } from '../models/groq.js'; -import { HuggingFace } from '../models/huggingface.js'; +import {mkdirSync, readFileSync, writeFileSync} from 'fs'; +import {Examples} from '../utils/examples.js'; +import {getCommand, getCommandDocs} from './commands/index.js'; +import {getSkillDocs} from './library/index.js'; +import {stringifyTurns} from '../utils/text.js'; +import {cosineSimilarity} from '../utils/math.js'; + +import {Gemini} from '../models/gemini.js'; +import {GPT} from '../models/gpt.js'; +import {Claude} from '../models/claude.js'; +import {ReplicateAPI} from '../models/replicate.js'; +import {Local} from '../models/local.js'; +import {GroqCloudAPI} from '../models/groq.js'; +import {HuggingFace} from '../models/huggingface.js'; export class Prompter { constructor(agent, fp) { @@ -19,7 +19,8 @@ export class Prompter { this.profile = JSON.parse(readFileSync(fp, 'utf8')); this.convo_examples = null; this.coding_examples = null; - + this.skill_docs_embeddings = {}; + let name = this.profile.name; let chat = this.profile.model; this.cooldown = this.profile.cooldown ? this.profile.cooldown : 0; @@ -111,16 +112,41 @@ export class Prompter { async initExamples() { // Using Promise.all to implement concurrent processing - // Create Examples instances this.convo_examples = new Examples(this.embedding_model); this.coding_examples = new Examples(this.embedding_model); - // Use Promise.all to load examples concurrently + let skill_docs = getSkillDocs(); await Promise.all([ this.convo_examples.load(this.profile.conversation_examples), this.coding_examples.load(this.profile.coding_examples), + ...skill_docs.map(async (doc) => { + let func_name_desc = doc.split('\n').slice(0, 2).join(''); + this.skill_docs_embeddings[doc] = await this.embedding_model.embed([func_name_desc]); + }), ]); } + async getRelevantSkillDocs(messages, select_num) { + let latest_message_content = messages.slice().reverse().find(msg => msg.role !== 'system')?.content || ''; + let latest_message_embedding = await this.embedding_model.embed([latest_message_content]); + + let skill_doc_similarities = Object.keys(this.skill_docs_embeddings) + .map(doc_key => ({ + doc_key, + similarity_score: cosineSimilarity(latest_message_embedding, this.skill_docs_embeddings[doc_key]) + })) + .sort((a, b) => b.similarity_score - a.similarity_score); + + // select_num = -1 means select all + let selected_docs = skill_doc_similarities.slice(0, select_num === -1 ? skill_doc_similarities.length : select_num); + let message = '\nThe following recommended functions are listed in descending order of task relevance.\nSkillDocs:\n'; + message += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); + return message; + } + + + + + async replaceStrings(prompt, messages, examples=null, to_summarize=[], last_goals=null) { prompt = prompt.replaceAll('$NAME', this.agent.name); @@ -135,7 +161,7 @@ export class Prompter { if (prompt.includes('$COMMAND_DOCS')) prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs()); if (prompt.includes('$CODE_DOCS')) - prompt = prompt.replaceAll('$CODE_DOCS', getSkillDocs()); + prompt = prompt.replaceAll('$CODE_DOCS', this.getRelevantSkillDocs(messages, -1)); if (prompt.includes('$EXAMPLES') && examples !== null) prompt = prompt.replaceAll('$EXAMPLES', await examples.createExampleMessage(messages)); if (prompt.includes('$MEMORY')) From f264b23ccc0cbe63c5d307717efdaf9ae6479f7e Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Fri, 1 Nov 2024 13:39:13 +0800 Subject: [PATCH 02/15] Add select_num exception range judgment --- src/agent/prompter.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 3ba51dd4..d44829e6 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -136,17 +136,19 @@ export class Prompter { })) .sort((a, b) => b.similarity_score - a.similarity_score); - // select_num = -1 means select all - let selected_docs = skill_doc_similarities.slice(0, select_num === -1 ? skill_doc_similarities.length : select_num); + let length = skill_doc_similarities.length; + if (typeof select_num !== 'number' || isNaN(select_num) || select_num <= 0) { + select_num = length; + } else { + select_num = Math.min(Math.floor(select_num), length); + } + let selected_docs = skill_doc_similarities.slice(0, select_num); let message = '\nThe following recommended functions are listed in descending order of task relevance.\nSkillDocs:\n'; message += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); + console.log(message); return message; } - - - - async replaceStrings(prompt, messages, examples=null, to_summarize=[], last_goals=null) { prompt = prompt.replaceAll('$NAME', this.agent.name); From 17fa2b6083dfc2bd218706895d38431b3536297d Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Fri, 1 Nov 2024 13:47:08 +0800 Subject: [PATCH 03/15] Add select_num exception range judgment --- src/agent/prompter.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/agent/prompter.js b/src/agent/prompter.js index d44829e6..530de3d0 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -137,7 +137,7 @@ export class Prompter { .sort((a, b) => b.similarity_score - a.similarity_score); let length = skill_doc_similarities.length; - if (typeof select_num !== 'number' || isNaN(select_num) || select_num <= 0) { + if (typeof select_num !== 'number' || isNaN(select_num) || select_num < 0) { select_num = length; } else { select_num = Math.min(Math.floor(select_num), length); @@ -145,7 +145,6 @@ export class Prompter { let selected_docs = skill_doc_similarities.slice(0, select_num); let message = '\nThe following recommended functions are listed in descending order of task relevance.\nSkillDocs:\n'; message += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); - console.log(message); return message; } @@ -163,7 +162,7 @@ export class Prompter { if (prompt.includes('$COMMAND_DOCS')) prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs()); if (prompt.includes('$CODE_DOCS')) - prompt = prompt.replaceAll('$CODE_DOCS', this.getRelevantSkillDocs(messages, -1)); + prompt = prompt.replaceAll('$CODE_DOCS', this.getRelevantSkillDocs(messages, 0)); if (prompt.includes('$EXAMPLES') && examples !== null) prompt = prompt.replaceAll('$EXAMPLES', await examples.createExampleMessage(messages)); if (prompt.includes('$MEMORY')) From ecaf5e87e14b8ba5430b49fdfff8b933a347e059 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sat, 2 Nov 2024 20:26:56 +0800 Subject: [PATCH 04/15] Code capability enhancement & bot crash fix --- package.json | 5 +++++ settings.js | 2 +- src/agent/coder.js | 42 +++++++++++++++++++++++++++++++++++++----- src/agent/prompter.js | 17 +++++++++-------- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index cd5b2729..8ba7e1ad 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,10 @@ "scripts": { "postinstall": "patch-package", "start": "node main.js" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" } } diff --git a/settings.js b/settings.js index 1de12c02..1b33d949 100644 --- a/settings.js +++ b/settings.js @@ -21,7 +21,7 @@ export default "show_bot_views": false, // show bot's view in browser at localhost:3000, 3001... "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk - "code_timeout_mins": 10, // minutes code is allowed to run. -1 for no timeout + "code_timeout_mins": 1, // minutes code is allowed to run. -1 for no timeout,set 1.Set 1 min to timely code adjustments "max_messages": 15, // max number of messages to keep in context "max_commands": -1, // max number of commands to use in a response. -1 for no limit diff --git a/src/agent/coder.js b/src/agent/coder.js index d3123878..fad23eab 100644 --- a/src/agent/coder.js +++ b/src/agent/coder.js @@ -1,6 +1,7 @@ import { writeFile, readFile, mkdirSync } from 'fs'; import { checkSafe } from '../utils/safety.js'; import settings from '../../settings.js'; +import {ESLint} from "eslint"; export class Coder { constructor(agent) { @@ -20,7 +21,31 @@ export class Coder { mkdirSync('.' + this.fp, { recursive: true }); } + + async checkCode(code) { + const eslint = new ESLint(); + const results = await eslint.lintText(code); + const codeLines = code.split('\n'); + let result = '#### CODE ERROR INFO ###\n'; + const exceptions = results.map(r => r.messages).flat(); + if (exceptions.length > 0) { + exceptions.forEach((exc, index) => { + if (exc.line && exc.column ) { + const errorLine = codeLines[exc.line - 1]?.trim() || 'Unable to retrieve error line content'; + result += `#ERROR ${index + 1}\n`; + result += `Message: ${exc.message}\n`; + result += `Location: Line ${exc.line}, Column ${exc.column}\n`; + result += `Related Code Line: ${errorLine}\n\n`; + } + }); + result += 'The code contains exceptions and cannot continue execution.\n'; + } else { + return null;//no error + } + + return result ; + } // write custom code to file and import it async stageCode(code) { code = this.sanitizeCode(code); @@ -48,12 +73,11 @@ export class Coder { this.file_counter++; let write_result = await this.writeFilePromise('.' + this.fp + filename, src) - if (write_result) { console.error('Error writing code execution file: ' + result); return null; } - return await import('../..' + this.fp + filename); + return {filename,src}; } sanitizeCode(code) { @@ -137,7 +161,14 @@ export class Coder { continue; } - const execution_file = await this.stageCode(code); + let {filename,src} = await this.stageCode(code); + const analysisResult = await this.checkCode(src); + if (analysisResult) { + const message = 'Error: Code syntax error. Please try again:'+'\n'+analysisResult+'\n'+await this.agent.prompter.getRelevantSkillDocs(analysisResult,3); + messages.push({ role: 'system', content: message }); + continue; + } + const execution_file = await import('../..' +this.fp+filename); if (!execution_file) { agent_history.add('system', 'Failed to stage code, something is wrong.'); return {success: false, message: null, interrupted: false, timedout: false}; @@ -219,10 +250,11 @@ export class Coder { this.executing = false; clearTimeout(TIMEOUT); this.cancelResume(); - console.error("Code execution triggered catch: " + err); await this.stop(); - let message = this.formatOutput(this.agent.bot) + '!!Code threw exception!! Error: ' + err; + err = err.toString(); + let relevant_skill_docs = await this.agent.prompter.getRelevantSkillDocs(err,5); + let message = this.formatOutput(this.agent.bot) + '!!Code threw exception!! Error: ' + err+'\n'+relevant_skill_docs; let interrupted = this.agent.bot.interrupt_code; this.clear(); if (!interrupted && !this.generating) this.agent.bot.emit('idle'); diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 530de3d0..317f4b64 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -125,9 +125,8 @@ export class Prompter { ]); } - async getRelevantSkillDocs(messages, select_num) { - let latest_message_content = messages.slice().reverse().find(msg => msg.role !== 'system')?.content || ''; - let latest_message_embedding = await this.embedding_model.embed([latest_message_content]); + async getRelevantSkillDocs(message, select_num) { + let latest_message_embedding = await this.embedding_model.embed(message); let skill_doc_similarities = Object.keys(this.skill_docs_embeddings) .map(doc_key => ({ @@ -143,9 +142,9 @@ export class Prompter { select_num = Math.min(Math.floor(select_num), length); } let selected_docs = skill_doc_similarities.slice(0, select_num); - let message = '\nThe following recommended functions are listed in descending order of task relevance.\nSkillDocs:\n'; - message += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); - return message; + let relevant_skill_docs = '####RELEVENT DOCS INFO###\nThe following functions are listed in descending order of relevance.\nSkillDocs:\n'; + relevant_skill_docs += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); + return relevant_skill_docs; } async replaceStrings(prompt, messages, examples=null, to_summarize=[], last_goals=null) { @@ -161,8 +160,10 @@ export class Prompter { } if (prompt.includes('$COMMAND_DOCS')) prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs()); - if (prompt.includes('$CODE_DOCS')) - prompt = prompt.replaceAll('$CODE_DOCS', this.getRelevantSkillDocs(messages, 0)); + if (prompt.includes('$CODE_DOCS')){ + let latest_message_content = messages.slice().reverse().find(msg => msg.role !== 'system')?.content || ''; + prompt = prompt.replaceAll('$CODE_DOCS', await this.getRelevantSkillDocs(latest_message_content, 5)); + } if (prompt.includes('$EXAMPLES') && examples !== null) prompt = prompt.replaceAll('$EXAMPLES', await examples.createExampleMessage(messages)); if (prompt.includes('$MEMORY')) From 5e84d69aeeab09711c2d309bbd275bf7e27b4e27 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 3 Nov 2024 03:52:00 +0800 Subject: [PATCH 05/15] Merger conflict resolution --- bots/codeChackTemplate.js | 10 ++++++++++ eslint.config.js | 25 +++++++++++++++++++++++++ src/agent/coder.js | 18 ++++++++++++------ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 bots/codeChackTemplate.js create mode 100644 eslint.config.js diff --git a/bots/codeChackTemplate.js b/bots/codeChackTemplate.js new file mode 100644 index 00000000..77b5d975 --- /dev/null +++ b/bots/codeChackTemplate.js @@ -0,0 +1,10 @@ +import * as skills from '../../../src/agent/library/skills.js'; +import * as world from '../../../src/agent/library/world.js'; +import Vec3 from 'vec3'; + +const log = skills.log; + +export async function main(bot) { + /* CODE HERE */ + log(bot, 'Code finished.'); +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..1bdf2b3d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,25 @@ +// eslint.config.js +import globals from "globals"; +import pluginJs from "@eslint/js"; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + // 首先引入推荐配置 + pluginJs.configs.recommended, + + // 然后覆盖或自定义特定规则 + { + languageOptions: { + globals: globals.browser, + ecmaVersion: 2021, + sourceType: "module", + }, + rules: { + "no-undef": "error", // 禁止使用未声明的变量或函数。 + "semi": ["error", "always"], // 强制在语句末尾使用分号。 + "curly": "warn", // 强制使用花括号包裹代码块。 + "no-unused-vars": "off", // 禁用未使用变量的警告。 + "no-unreachable": "off", // 禁用无法到达代码的警告。 + }, + }, +]; diff --git a/src/agent/coder.js b/src/agent/coder.js index d5bbf384..729bb03b 100644 --- a/src/agent/coder.js +++ b/src/agent/coder.js @@ -14,6 +14,7 @@ export class Coder { this.executing = false; this.generating = false; this.code_template = ''; + this.code_chack_template = ''; this.timedout = false; this.cur_action_name = ''; @@ -21,7 +22,10 @@ export class Coder { if (err) throw err; this.code_template = data; }); - + readFile('./bots/codeChackTemplate.js', 'utf8', (err, data) => { + if (err) throw err; + this.code_chack_template = data; + }); mkdirSync('.' + this.fp, { recursive: true }); } @@ -64,6 +68,7 @@ export class Coder { for (let line of code.split('\n')) { src += ` ${line}\n`; } + let src_check_copy = this.code_chack_template.replace('/* CODE HERE */', src); src = this.code_template.replace('/* CODE HERE */', src); let filename = this.file_counter + '.js'; @@ -92,8 +97,7 @@ export class Coder { console.error('Error writing code execution file: ' + result); return null; } - - return [ main: mainFn ,src]; + return { func:{main: mainFn}, src_check_copy: src_check_copy }; } sanitizeCode(code) { @@ -170,10 +174,12 @@ export class Coder { } code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); - let codeStagingResult,src; + let codeStagingResult,src_check_copy; try { - [codeStagingResult,src] = await this.stageCode(code); - const analysisResult = await this.checkCode(src); + const result = await this.stageCode(code); + codeStagingResult = result.func; + src_check_copy = result.src_check_copy; + const analysisResult = await this.checkCode(src_check_copy); if (analysisResult) { const message = 'Error: Code syntax error. Please try again:'+'\n'+analysisResult+'\n'+await this.agent.prompter.getRelevantSkillDocs(analysisResult,3); messages.push({ role: 'system', content: message }); From e1dfad90803a08a51062063c6ad0c1788d7ad94e Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 3 Nov 2024 04:01:11 +0800 Subject: [PATCH 06/15] Change note to English --- eslint.config.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 1bdf2b3d..e1506fd3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,10 +4,10 @@ import pluginJs from "@eslint/js"; /** @type {import('eslint').Linter.Config[]} */ export default [ - // 首先引入推荐配置 + // First, import the recommended configuration pluginJs.configs.recommended, - // 然后覆盖或自定义特定规则 + // Then override or customize specific rules { languageOptions: { globals: globals.browser, @@ -15,11 +15,11 @@ export default [ sourceType: "module", }, rules: { - "no-undef": "error", // 禁止使用未声明的变量或函数。 - "semi": ["error", "always"], // 强制在语句末尾使用分号。 - "curly": "warn", // 强制使用花括号包裹代码块。 - "no-unused-vars": "off", // 禁用未使用变量的警告。 - "no-unreachable": "off", // 禁用无法到达代码的警告。 + "no-undef": "error", // Disallow the use of undeclared variables or functions. + "semi": ["error", "always"], // Require the use of semicolons at the end of statements. + "curly": "warn", // Enforce the use of curly braces around blocks of code. + "no-unused-vars": "off", // Disable warnings for unused variables. + "no-unreachable": "off", // Disable warnings for unreachable code. }, }, ]; From 82b37e02aab56559f148253a0cdd0592c943c8d6 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Tue, 5 Nov 2024 01:08:56 +0800 Subject: [PATCH 07/15] Resolving merge conflicts with Task Manager --- src/agent/action_manager.js | 4 +++- src/agent/coder.js | 46 ++++++++++++++++++++++++------------- src/agent/prompter.js | 9 +++++--- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/agent/action_manager.js b/src/agent/action_manager.js index 833f3c0f..5133a6bc 100644 --- a/src/agent/action_manager.js +++ b/src/agent/action_manager.js @@ -111,7 +111,9 @@ export class ActionManager { console.error("Code execution triggered catch: " + err); await this.stop(); - let message = this._getBotOutputSummary() + '!!Code threw exception!! Error: ' + err; + err = err.toString(); + let relevant_skill_docs = await this.agent.prompter.getRelevantSkillDocs(err,5); + let message = this._getBotOutputSummary() + '!!Code threw exception!! Error: ' + err+'\n'+relevant_skill_docs; let interrupted = this.agent.bot.interrupt_code; this.agent.clearBotLogs(); if (!interrupted && !this.agent.coder.generating) { diff --git a/src/agent/coder.js b/src/agent/coder.js index 36742d7a..a4c18f60 100644 --- a/src/agent/coder.js +++ b/src/agent/coder.js @@ -27,10 +27,28 @@ export class Coder { } async checkCode(code) { + let result = '#### CODE ERROR INFO ###\n'; + // Extract everything in the code between the beginning of 'skills./world.' and the '(' + const skillRegex = /(?:skills|world)\.(.*?)\(/g; + const skills = []; + let match; + while ((match = skillRegex.exec(code)) !== null) { + skills.push(match[1]); + } + const allDocs = await this.agent.prompter.getRelevantSkillDocs(); + //Check if the function exists + const missingSkills = skills.filter(skill => !allDocs.includes(skill)); + if (missingSkills.length > 0) { + result += 'These functions do not exist. Please modify the correct function name and try again.\n'; + result += '### FUNCTIONS NOT FOUND ###\n'; + result += missingSkills.join('\n'); + console.log(result) + return result; + } + const eslint = new ESLint(); const results = await eslint.lintText(code); const codeLines = code.split('\n'); - let result = '#### CODE ERROR INFO ###\n'; const exceptions = results.map(r => r.messages).flat(); if (exceptions.length > 0) { @@ -40,10 +58,10 @@ export class Coder { result += `#ERROR ${index + 1}\n`; result += `Message: ${exc.message}\n`; result += `Location: Line ${exc.line}, Column ${exc.column}\n`; - result += `Related Code Line: ${errorLine}\n\n`; + result += `Related Code Line: ${errorLine}\n`; } }); - result += 'The code contains exceptions and cannot continue execution.\n'; + result += 'The code contains exceptions and cannot continue execution.'; } else { return null;//no error } @@ -172,14 +190,14 @@ export class Coder { code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); const result = await this.stageCode(code); const executionModuleExports = result.func; + let src_check_copy = result.src_check_copy; + const analysisResult = await this.checkCode(src_check_copy); + if (analysisResult) { + const message = 'Error: Code syntax error. Please try again:'+'\n'+analysisResult+'\n'+await this.agent.prompter.getRelevantSkillDocs(analysisResult,3); + messages.push({ role: 'system', content: message }); + continue; + } if (!executionModuleExports) { - let src_check_copy = result.src_check_copy; - const analysisResult = await this.checkCode(src_check_copy); - if (analysisResult) { - const message = 'Error: Code syntax error. Please try again:'+'\n'+analysisResult+'\n'+await this.agent.prompter.getRelevantSkillDocs(analysisResult,3); - messages.push({ role: 'system', content: message }); - continue; - } agent_history.add('system', 'Failed to stage code, something is wrong.'); return {success: false, message: null, interrupted: false, timedout: false}; } @@ -189,10 +207,10 @@ export class Coder { }, { timeout: settings.code_timeout_mins }); if (code_return.interrupted && !code_return.timedout) return { success: false, message: null, interrupted: true, timedout: false }; - console.log("Code generation result:", code_return.success, code_return.message); + console.log("Code generation result:", code_return.success, code_return.message.toString()); if (code_return.success) { - const summary = "Summary of newAction\nAgent wrote this code: \n```" + this.sanitizeCode(code) + "```\nCode Output:\n" + code_return.message; + const summary = "Summary of newAction\nAgent wrote this code: \n```" + this.sanitizeCode(code) + "```\nCode Output:\n" + code_return.message.toString(); return { success: true, message: summary, interrupted: false, timedout: false }; } @@ -207,8 +225,4 @@ export class Coder { } return { success: false, message: null, interrupted: false, timedout: true }; } -//err = err.toString(); -// let relevant_skill_docs = await this.agent.prompter.getRelevantSkillDocs(err,5); -// let message = this.formatOutput(this.agent.bot) + '!!Code threw exception!! Error: ' + err+'\n'+relevant_skill_docs; -// } \ No newline at end of file diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 48649f91..0ef02f80 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -133,7 +133,9 @@ export class Prompter { } async getRelevantSkillDocs(message, select_num) { - let latest_message_embedding = await this.embedding_model.embed(message); + let latest_message_embedding = ''; + if(message) //message is not empty, get the relevant skill docs, else return all skill docs + latest_message_embedding = await this.embedding_model.embed(message); let skill_doc_similarities = Object.keys(this.skill_docs_embeddings) .map(doc_key => ({ @@ -149,8 +151,9 @@ export class Prompter { select_num = Math.min(Math.floor(select_num), length); } let selected_docs = skill_doc_similarities.slice(0, select_num); - let relevant_skill_docs = '####RELEVENT DOCS INFO###\nThe following functions are listed in descending order of relevance.\nSkillDocs:\n'; - relevant_skill_docs += selected_docs.map(doc => `${doc.doc_key}`).join('\n'); + let relevant_skill_docs = '#### RELEVENT DOCS INFO ###\nThe following functions are listed in descending order of relevance.\n'; + relevant_skill_docs += 'SkillDocs:\n' + relevant_skill_docs += '###'+ selected_docs.map(doc => `${doc.doc_key}`).join('\n'); return relevant_skill_docs; } From f6e309a6bcd640e5056a61991fd36740b6f8f574 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Wed, 6 Nov 2024 12:25:11 +0800 Subject: [PATCH 08/15] Fix spelling mistakes --- bots/{codeChackTemplate.js => codeCheckTemplate.js} | 0 src/agent/coder.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename bots/{codeChackTemplate.js => codeCheckTemplate.js} (100%) diff --git a/bots/codeChackTemplate.js b/bots/codeCheckTemplate.js similarity index 100% rename from bots/codeChackTemplate.js rename to bots/codeCheckTemplate.js diff --git a/src/agent/coder.js b/src/agent/coder.js index a4c18f60..d418829a 100644 --- a/src/agent/coder.js +++ b/src/agent/coder.js @@ -19,7 +19,7 @@ export class Coder { if (err) throw err; this.code_template = data; }); - readFile('./bots/codeChackTemplate.js', 'utf8', (err, data) => { + readFile('./bots/codeCheckTemplate.js', 'utf8', (err, data) => { if (err) throw err; this.code_chack_template = data; }); From e15c5164756c1fe4d6b399af89dc0bf428843ec3 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Fri, 8 Nov 2024 18:43:54 +0800 Subject: [PATCH 09/15] Resolving conflicts created by adding new annotations --- src/agent/prompter.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 2ac98b3b..06390e1d 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -129,7 +129,7 @@ export class Prompter { this.convo_examples = new Examples(this.embedding_model); this.coding_examples = new Examples(this.embedding_model); - const [convoResult, codingResult] = await Promise.allSettled([ + const results = await Promise.allSettled([ this.convo_examples.load(this.profile.conversation_examples), this.coding_examples.load(this.profile.coding_examples), ...getSkillDocs().map(async (doc) => { @@ -138,7 +138,9 @@ export class Prompter { }) ]); - // Handle potential failures + // Handle potential failures for conversation and coding examples + const [convoResult, codingResult, ...skillDocResults] = results; + if (convoResult.status === 'rejected') { console.error('Failed to load conversation examples:', convoResult.reason); throw convoResult.reason; @@ -147,6 +149,12 @@ export class Prompter { console.error('Failed to load coding examples:', codingResult.reason); throw codingResult.reason; } + skillDocResults.forEach((result, index) => { + if (result.status === 'rejected') { + console.error(`Failed to load skill doc ${index + 1}:`, result.reason); + } + }); + } catch (error) { console.error('Failed to initialize examples:', error); throw error; From c8302c27ac2c61a3b765a89617be38642f87d694 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sat, 9 Nov 2024 01:29:24 +0800 Subject: [PATCH 10/15] Improve the relevance of docs to !newAction("task") --- src/agent/action_manager.js | 2 +- src/agent/prompter.js | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/agent/action_manager.js b/src/agent/action_manager.js index 5133a6bc..c016e70e 100644 --- a/src/agent/action_manager.js +++ b/src/agent/action_manager.js @@ -133,7 +133,7 @@ export class ActionManager { First outputs:\n${output.substring(0, MAX_OUT / 2)}\n...skipping many lines.\nFinal outputs:\n ${output.substring(output.length - MAX_OUT / 2)}`; } else { - output = 'Code output:\n' + output; + output = 'Code output:\n' + output.toString(); } return output; } diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 06390e1d..5952ac63 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -134,7 +134,7 @@ export class Prompter { this.coding_examples.load(this.profile.coding_examples), ...getSkillDocs().map(async (doc) => { let func_name_desc = doc.split('\n').slice(0, 2).join(''); - this.skill_docs_embeddings[doc] = await this.embedding_model.embed([func_name_desc]); + this.skill_docs_embeddings[doc] = await this.embedding_model.embed(func_name_desc); }) ]); @@ -199,9 +199,20 @@ export class Prompter { } if (prompt.includes('$COMMAND_DOCS')) prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs()); - if (prompt.includes('$CODE_DOCS')){ - let latest_message_content = messages.slice().reverse().find(msg => msg.role !== 'system')?.content || ''; - prompt = prompt.replaceAll('$CODE_DOCS', await this.getRelevantSkillDocs(latest_message_content, 5)); + if (prompt.includes('$CODE_DOCS')) { + // Find the most recent non-system message containing '!newAction(' + let code_task_content = messages.slice().reverse().find(msg => + msg.role !== 'system' && msg.content.includes('!newAction(') + )?.content || ''; + + // Extract content between '!newAction(' and ')' + const match = code_task_content.match(/!newAction\((.*?)\)/); + code_task_content = match ? match[1] : ''; + + prompt = prompt.replaceAll( + '$CODE_DOCS', + await this.getRelevantSkillDocs(code_task_content, 5) + ); } if (prompt.includes('$EXAMPLES') && examples !== null) prompt = prompt.replaceAll('$EXAMPLES', await examples.createExampleMessage(messages)); From a3684516147bb1a52aee951652d6839c42c9d2fc Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sat, 9 Nov 2024 01:29:57 +0800 Subject: [PATCH 11/15] Fix Qwen api concurrency limit issue --- src/models/qwen.js | 76 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/src/models/qwen.js b/src/models/qwen.js index d3d7beca..d5462984 100644 --- a/src/models/qwen.js +++ b/src/models/qwen.js @@ -49,12 +49,12 @@ export class Qwen { async embed(text) { if (!text || typeof text !== 'string') { - console.error('Invalid embedding input: text must be a non-empty string.'); + console.error('Invalid embedding input: text must be a non-empty string:', text); return 'Invalid embedding input: text must be a non-empty string.'; } const data = { - model: 'text-embedding-v2', + model: this.modelName, input: { texts: [text] }, parameters: { text_type: 'query' }, }; @@ -67,38 +67,68 @@ export class Qwen { try { const response = await this._makeHttpRequest(this.url, data); const embedding = response?.output?.embeddings?.[0]?.embedding; + return embedding || 'No embedding result received.'; } catch (err) { - console.error('Error occurred:', err); + console.log('Embed data:', data); + console.error('Embed error occurred:', err); return 'An error occurred, please try again.'; } } - async _makeHttpRequest(url, data) { + async _makeHttpRequest(url, data, maxRetries = 10) { const headers = { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }; - const response = await fetch(url, { - method: 'POST', - headers, - body: JSON.stringify(data), - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`Request failed, status code ${response.status}: ${response.statusText}`); - console.error('Error response content:', errorText); - throw new Error(`Request failed, status code ${response.status}: ${response.statusText}`); - } - - const responseText = await response.text(); - try { - return JSON.parse(responseText); - } catch (err) { - console.error('Failed to parse response JSON:', err); - throw new Error('Invalid response JSON format.'); + let retryCount = 0; + + while (retryCount < maxRetries) { + try { + const response = await fetch(url, { + method: 'POST', + headers, + body: JSON.stringify(data), + }); + + if (response.ok) { + const responseText = await response.text(); + try { + //Task completed successfully + return JSON.parse(responseText); + } catch (err) { + console.error('Failed to parse response JSON:', err); + throw new Error('Invalid response JSON format.'); + } + } else { + const errorText = await response.text(); + + if (response.status === 429 || response.statusText.includes('Too Many Requests')) { + // Handle rate limiting + retryCount++; + if (retryCount >= maxRetries) { + console.error('Exceeded maximum retry attempts, unable to get request result.'); + throw new Error(`Request failed after ${maxRetries} retries due to rate limiting.`); + } + //Reached Qwen concurrency limit, waiting in queue + const waitTime = Math.random() * 1000; // Random wait between 0 to 1 seconds + await new Promise(resolve => setTimeout(resolve, waitTime)); + continue; // Retry the request + } else { + console.error(`Request failed, status code ${response.status}: ${response.statusText}`); + console.error('Error response content:', errorText); + throw new Error(`Request failed, status code ${response.status}: ${response.statusText}`); + } + } + } catch (err) { + // Handle network errors or other exceptions + console.error('Error occurred during HTTP request:', err); + throw err; // Re-throw the error to be handled by the caller + } } + // Exceeded maximum retries + console.error('Exceeded maximum retry attempts, unable to get request result.'); + throw new Error(`Request failed after ${maxRetries} retries.`); } } From 2322f7857e36590a9409d4fe9d89d034c732de9d Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 10 Nov 2024 17:46:18 +0800 Subject: [PATCH 12/15] code_timeout_mins is set to 3 --- settings.js | 10 +++++----- src/agent/prompter.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/settings.js b/settings.js index 50f11d7f..aa036c1e 100644 --- a/settings.js +++ b/settings.js @@ -6,23 +6,23 @@ export default "auth": "offline", // or "microsoft" "profiles": [ - "./andy.json", + //"./andy.json", // "./profiles/gpt.json", // "./profiles/claude.json", // "./profiles/gemini.json", // "./profiles/llama.json", - // "./profiles/qwen.json", + "./profiles/qwen.json", // using more than 1 profile requires you to /msg each bot indivually ], - "load_memory": false, // load memory from previous session + "load_memory": true, // load memory from previous session "init_message": "Say hello world and your name", // sends to all on spawn "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages "show_bot_views": false, // show bot's view in browser at localhost:3000, 3001... - "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk - "code_timeout_mins": 1, // minutes code is allowed to run. -1 for no timeout,set 1.Set 1 min to timely code adjustments + "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk + "code_timeout_mins": 3, // minutes code is allowed to run. -1 for no timeout,set 1.Set 1 min to timely code adjustments "max_messages": 15, // max number of messages to keep in context "max_commands": -1, // max number of commands to use in a response. -1 for no limit diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 5952ac63..bf81a106 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -182,7 +182,7 @@ export class Prompter { let selected_docs = skill_doc_similarities.slice(0, select_num); let relevant_skill_docs = '#### RELEVENT DOCS INFO ###\nThe following functions are listed in descending order of relevance.\n'; relevant_skill_docs += 'SkillDocs:\n' - relevant_skill_docs += '###'+ selected_docs.map(doc => `${doc.doc_key}`).join('\n'); + relevant_skill_docs += selected_docs.map(doc => `${doc.doc_key}`).join('\n### '); return relevant_skill_docs; } From dd176afc71560b00f9062c6a6b0a7e3ab0c57ef7 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 10 Nov 2024 17:50:30 +0800 Subject: [PATCH 13/15] set default profiles to andy.json --- settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.js b/settings.js index aa036c1e..b23feb19 100644 --- a/settings.js +++ b/settings.js @@ -6,12 +6,12 @@ export default "auth": "offline", // or "microsoft" "profiles": [ - //"./andy.json", + "./andy.json", // "./profiles/gpt.json", // "./profiles/claude.json", // "./profiles/gemini.json", // "./profiles/llama.json", - "./profiles/qwen.json", + // "./profiles/qwen.json", // using more than 1 profile requires you to /msg each bot indivually ], From 69c0bd158467d23ec791ced4ff861ec93bb8e354 Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 10 Nov 2024 17:52:19 +0800 Subject: [PATCH 14/15] Default settings except code_timeout_mins --- settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.js b/settings.js index b23feb19..46d529f3 100644 --- a/settings.js +++ b/settings.js @@ -15,13 +15,13 @@ export default // using more than 1 profile requires you to /msg each bot indivually ], - "load_memory": true, // load memory from previous session + "load_memory": false, // load memory from previous session "init_message": "Say hello world and your name", // sends to all on spawn "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages "show_bot_views": false, // show bot's view in browser at localhost:3000, 3001... - "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk + "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk "code_timeout_mins": 3, // minutes code is allowed to run. -1 for no timeout,set 1.Set 1 min to timely code adjustments "max_messages": 15, // max number of messages to keep in context From cba7f7b431d61219083c6d970d331eb19322bdba Mon Sep 17 00:00:00 2001 From: Qu Yi Date: Sun, 10 Nov 2024 17:58:23 +0800 Subject: [PATCH 15/15] Default settings except code_timeout_mins --- settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.js b/settings.js index 46d529f3..b0908386 100644 --- a/settings.js +++ b/settings.js @@ -22,7 +22,7 @@ export default "show_bot_views": false, // show bot's view in browser at localhost:3000, 3001... "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk - "code_timeout_mins": 3, // minutes code is allowed to run. -1 for no timeout,set 1.Set 1 min to timely code adjustments + "code_timeout_mins": 3, // minutes code is allowed to run. -1 for no timeout,set 3.Set 3 min to timely code adjustments "max_messages": 15, // max number of messages to keep in context "max_commands": -1, // max number of commands to use in a response. -1 for no limit