You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
6.5 KiB

11 months ago
  1. const { v4 } = require("uuid");
  2. const { getVectorDbClass, getLLMProvider } = require("../../../helpers");
  3. const { Deduplicator } = require("../utils/dedupe");
  4. const memory = {
  5. name: "rag-memory",
  6. startupConfig: {
  7. params: {},
  8. },
  9. plugin: function () {
  10. return {
  11. name: this.name,
  12. setup(aibitat) {
  13. aibitat.function({
  14. super: aibitat,
  15. tracker: new Deduplicator(),
  16. name: this.name,
  17. description:
  18. "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.",
  19. examples: [
  20. {
  21. prompt: "What is AnythingLLM?",
  22. call: JSON.stringify({
  23. action: "search",
  24. content: "What is AnythingLLM?",
  25. }),
  26. },
  27. {
  28. prompt: "What do you know about Plato's motives?",
  29. call: JSON.stringify({
  30. action: "search",
  31. content: "What are the facts about Plato's motives?",
  32. }),
  33. },
  34. {
  35. prompt: "Remember that you are a robot",
  36. call: JSON.stringify({
  37. action: "store",
  38. content: "I am a robot, the user told me that i am.",
  39. }),
  40. },
  41. {
  42. prompt: "Save that to memory please.",
  43. call: JSON.stringify({
  44. action: "store",
  45. content: "<insert summary of conversation until now>",
  46. }),
  47. },
  48. ],
  49. parameters: {
  50. $schema: "http://json-schema.org/draft-07/schema#",
  51. type: "object",
  52. properties: {
  53. action: {
  54. type: "string",
  55. enum: ["search", "store"],
  56. description:
  57. "The action we want to take to search for existing similar context or storage of new context.",
  58. },
  59. content: {
  60. type: "string",
  61. description:
  62. "The plain text to search our local documents with or to store in our vector database.",
  63. },
  64. },
  65. additionalProperties: false,
  66. },
  67. handler: async function ({ action = "", content = "" }) {
  68. try {
  69. if (this.tracker.isDuplicate(this.name, { action, content }))
  70. return `This was a duplicated call and it's output will be ignored.`;
  71. let response = "There was nothing to do.";
  72. if (action === "search") response = await this.search(content);
  73. if (action === "store") response = await this.store(content);
  74. this.tracker.trackRun(this.name, { action, content });
  75. return response;
  76. } catch (error) {
  77. console.log(error);
  78. return `There was an error while calling the function. ${error.message}`;
  79. }
  80. },
  81. search: async function (query = "") {
  82. try {
  83. const workspace = this.super.handlerProps.invocation.workspace;
  84. const LLMConnector = getLLMProvider({
  85. provider: workspace?.chatProvider,
  86. model: workspace?.chatModel,
  87. });
  88. const vectorDB = getVectorDbClass();
  89. const { contextTexts = [] } =
  90. await vectorDB.performSimilaritySearch({
  91. namespace: workspace.slug,
  92. input: query,
  93. LLMConnector,
  94. topN: workspace?.topN ?? 4,
  95. rerank: workspace?.vectorSearchMode === "rerank",
  96. });
  97. if (contextTexts.length === 0) {
  98. this.super.introspect(
  99. `${this.caller}: I didn't find anything locally that would help answer this question.`
  100. );
  101. return "There was no additional context found for that query. We should search the web for this information.";
  102. }
  103. this.super.introspect(
  104. `${this.caller}: Found ${contextTexts.length} additional piece of context to help answer this question.`
  105. );
  106. let combinedText = "Additional context for query:\n";
  107. for (const text of contextTexts) combinedText += text + "\n\n";
  108. return combinedText;
  109. } catch (error) {
  110. this.super.handlerProps.log(
  111. `memory.search raised an error. ${error.message}`
  112. );
  113. return `An error was raised while searching the vector database. ${error.message}`;
  114. }
  115. },
  116. store: async function (content = "") {
  117. try {
  118. const workspace = this.super.handlerProps.invocation.workspace;
  119. const vectorDB = getVectorDbClass();
  120. const { error } = await vectorDB.addDocumentToNamespace(
  121. workspace.slug,
  122. {
  123. docId: v4(),
  124. id: v4(),
  125. url: "file://embed-via-agent.txt",
  126. title: "agent-memory.txt",
  127. docAuthor: "@agent",
  128. description: "Unknown",
  129. docSource: "a text file stored by the workspace agent.",
  130. chunkSource: "",
  131. published: new Date().toLocaleString(),
  132. wordCount: content.split(" ").length,
  133. pageContent: content,
  134. token_count_estimate: 0,
  135. },
  136. null
  137. );
  138. if (!!error)
  139. return "The content was failed to be embedded properly.";
  140. this.super.introspect(
  141. `${this.caller}: I saved the content to long-term memory in this workspaces vector database.`
  142. );
  143. return "The content given was successfully embedded. There is nothing else to do.";
  144. } catch (error) {
  145. this.super.handlerProps.log(
  146. `memory.store raised an error. ${error.message}`
  147. );
  148. return `Let the user know this action was not successful. An error was raised while storing data in the vector database. ${error.message}`;
  149. }
  150. },
  151. });
  152. },
  153. };
  154. },
  155. };
  156. module.exports = {
  157. memory,
  158. };