Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions src/agent/coder.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,28 @@ export class Coder {

async _lintCode(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 = [];
// Extract namespace-qualified function calls like skills.placeBlock(...)
// and world.getNearestFreeSpace(...). Keeping namespace prevents
// collisions between similarly named functions.
const skillRegex = /\b(skills|world)\.([A-Za-z_$][\w$]*)\s*\(/g;
const referencedFunctions = [];
let match;
while ((match = skillRegex.exec(code)) !== null) {
skills.push(match[1]);
referencedFunctions.push({
namespace: match[1],
name: match[2],
});
}
const allDocs = await this.agent.prompter.skill_libary.getAllSkillDocs();
const availableFunctions = await this.agent.prompter.skill_libary.getAvailableFunctionsByNamespace();
// check function exists
const missingSkills = skills.filter(skill => !!allDocs[skill]);
const missingSkills = referencedFunctions
.filter(fn => !(availableFunctions[fn.namespace]?.has(fn.name)))
.map(fn => `${fn.namespace}.${fn.name}`);
if (missingSkills.length > 0) {
result += 'These functions do not exist.\n';
result += '### FUNCTIONS NOT FOUND ###\n';
result += missingSkills.join('\n');
console.log(result)
console.log(result);
return result;
}

Expand Down Expand Up @@ -192,19 +199,19 @@ export class Coder {
const mainFn = compartment.evaluate(src);

if (write_result) {
console.error('Error writing code execution file: ' + result);
console.error('Error writing code execution file: ' + write_result);
return null;
}
return { func:{main: mainFn}, src_lint_copy: src_lint_copy };
}

_sanitizeCode(code) {
code = code.trim();
const remove_strs = ['Javascript', 'javascript', 'js']
const remove_strs = ['Javascript', 'javascript', 'js'];
for (let r of remove_strs) {
if (code.startsWith(r)) {
code = code.slice(r.length);
return code;
break;
}
}
return code;
Expand All @@ -222,4 +229,4 @@ export class Coder {
});
});
}
}
}
34 changes: 28 additions & 6 deletions src/agent/library/skill_library.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,32 @@ export class SkillLibrary {
this.embedding_model = embedding_model;
this.skill_docs_embeddings = {};
this.skill_docs = null;
this.always_show_skills = ['skills.placeBlock', 'skills.wait', 'skills.breakBlockAt']
this.available_functions_by_namespace = {
skills: new Set(),
world: new Set(),
};
this.always_show_functions = [
{ namespace: 'skills', name: 'placeBlock' },
{ namespace: 'skills', name: 'wait' },
{ namespace: 'skills', name: 'breakBlockAt' },
];
}
async initSkillLibrary() {
const skillDocs = getSkillDocs();
this.skill_docs = skillDocs;
this.available_functions_by_namespace = {
skills: new Set(),
world: new Set(),
};
for (const doc of skillDocs) {
const qualifiedName = doc.split('\n')[0]?.trim();
const match = qualifiedName?.match(/^(skills|world)\.([A-Za-z_$][\w$]*)$/);
if (match) {
const namespace = match[1];
const methodName = match[2];
this.available_functions_by_namespace[namespace].add(methodName);
}
}
if (this.embedding_model) {
try {
const embeddingPromises = skillDocs.map((doc) => {
Expand All @@ -28,19 +49,20 @@ export class SkillLibrary {
}
}
this.always_show_skills_docs = {};
for (const skillName of this.always_show_skills) {
this.always_show_skills_docs[skillName] = this.skill_docs.find(doc => doc.includes(skillName));
for (const func of this.always_show_functions) {
const qualifiedName = `${func.namespace}.${func.name}`;
this.always_show_skills_docs[qualifiedName] = this.skill_docs.find(doc => doc.startsWith(`${qualifiedName}\n`) || doc === qualifiedName);
}
}

async getAllSkillDocs() {
return this.skill_docs;
getAvailableFunctionsByNamespace() {
return this.available_functions_by_namespace;
}

async getRelevantSkillDocs(message, select_num) {
if(!message) // use filler message if none is provided
message = '(no message)';
let skill_doc_similarities = [];
let skill_doc_similarities;

if (select_num === -1) {
skill_doc_similarities = Object.keys(this.skill_docs_embeddings)
Expand Down