anything-llm/server/utils/agents/aibitat/plugins/memory.js

166 lines
6.5 KiB
JavaScript
Raw Normal View History

const { v4 } = require("uuid");
const { getVectorDbClass, getLLMProvider } = require("../../../helpers");
const { Deduplicator } = require("../utils/dedupe");
const memory = {
name: "rag-memory",
startupConfig: {
params: {},
},
plugin: function () {
return {
name: this.name,
setup(aibitat) {
aibitat.function({
super: aibitat,
tracker: new Deduplicator(),
name: this.name,
description:
"Search against local documents for context that is relevant to the query or store a snippet of text into memory for retrieval later. Storing information should only be done when the user specifically requests for information to be remembered or saved to long-term memory. You should use this tool before search the internet for information. Do not use this tool unless you are explicity told to 'remember' or 'store' information.",
examples: [
{
prompt: "What is AnythingLLM?",
call: JSON.stringify({
action: "search",
content: "What is AnythingLLM?",
}),
},
{
prompt: "What do you know about Plato's motives?",
call: JSON.stringify({
action: "search",
content: "What are the facts about Plato's motives?",
}),
},
{
prompt: "Remember that you are a robot",
call: JSON.stringify({
action: "store",
content: "I am a robot, the user told me that i am.",
}),
},
{
prompt: "Save that to memory please.",
call: JSON.stringify({
action: "store",
content: "<insert summary of conversation until now>",
}),
},
],
parameters: {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: {
action: {
type: "string",
enum: ["search", "store"],
description:
"The action we want to take to search for existing similar context or storage of new context.",
},
content: {
type: "string",
description:
"The plain text to search our local documents with or to store in our vector database.",
},
},
additionalProperties: false,
},
handler: async function ({ action = "", content = "" }) {
try {
if (this.tracker.isDuplicate(this.name, { action, content }))
return `This was a duplicated call and it's output will be ignored.`;
let response = "There was nothing to do.";
if (action === "search") response = await this.search(content);
if (action === "store") response = await this.store(content);
this.tracker.trackRun(this.name, { action, content });
return response;
} catch (error) {
console.log(error);
return `There was an error while calling the function. ${error.message}`;
}
},
search: async function (query = "") {
try {
const workspace = this.super.handlerProps.invocation.workspace;
const LLMConnector = getLLMProvider({
provider: workspace?.chatProvider,
model: workspace?.chatModel,
});
const vectorDB = getVectorDbClass();
const { contextTexts = [] } =
await vectorDB.performSimilaritySearch({
namespace: workspace.slug,
input: query,
LLMConnector,
topN: workspace?.topN ?? 4,
});
if (contextTexts.length === 0) {
this.super.introspect(
`${this.caller}: I didn't find anything locally that would help answer this question.`
);
return "There was no additional context found for that query. We should search the web for this information.";
}
this.super.introspect(
`${this.caller}: Found ${contextTexts.length} additional piece of context to help answer this question.`
);
let combinedText = "Additional context for query:\n";
for (const text of contextTexts) combinedText += text + "\n\n";
return combinedText;
} catch (error) {
this.super.handlerProps.log(
`memory.search raised an error. ${error.message}`
);
return `An error was raised while searching the vector database. ${error.message}`;
}
},
store: async function (content = "") {
try {
const workspace = this.super.handlerProps.invocation.workspace;
const vectorDB = getVectorDbClass();
const { error } = await vectorDB.addDocumentToNamespace(
workspace.slug,
{
docId: v4(),
id: v4(),
url: "file://embed-via-agent.txt",
title: "agent-memory.txt",
docAuthor: "@agent",
description: "Unknown",
docSource: "a text file stored by the workspace agent.",
chunkSource: "",
published: new Date().toLocaleString(),
wordCount: content.split(" ").length,
pageContent: content,
token_count_estimate: 0,
},
null
);
if (!!error)
return "The content was failed to be embedded properly.";
this.super.introspect(
`${this.caller}: I saved the content to long-term memory in this workspaces vector database.`
);
return "The content given was successfully embedded. There is nothing else to do.";
} catch (error) {
this.super.handlerProps.log(
`memory.store raised an error. ${error.message}`
);
return `Let the user know this action was not successful. An error was raised while storing data in the vector database. ${error.message}`;
}
},
});
},
};
},
};
module.exports = {
memory,
};