anything-llm/server/utils/agents/aibitat/plugins/summarize.js
Timothy Carambat 99f2c25b1c
Agent Context window + context window refactor. (#2126)
* Enable agent context windows to be accurate per provider:model

* Refactor model mapping to external file
Add token count to document length instead of char-count
refernce promptWindowLimit from AIProvider in central location

* remove unused imports
2024-08-15 12:13:28 -07:00

181 lines
6.5 KiB
JavaScript

const { Document } = require("../../../../models/documents");
const { safeJsonParse } = require("../../../http");
const { summarizeContent } = require("../utils/summarize");
const Provider = require("../providers/ai-provider");
const docSummarizer = {
name: "document-summarizer",
startupConfig: {
params: {},
},
plugin: function () {
return {
name: this.name,
setup(aibitat) {
aibitat.function({
super: aibitat,
name: this.name,
controller: new AbortController(),
description:
"Can get the list of files available to search with descriptions and can select a single file to open and summarize.",
examples: [
{
prompt: "Summarize example.txt",
call: JSON.stringify({
action: "summarize",
document_filename: "example.txt",
}),
},
{
prompt: "What files can you see?",
call: JSON.stringify({ action: "list", document_filename: null }),
},
{
prompt: "Tell me about readme.md",
call: JSON.stringify({
action: "summarize",
document_filename: "readme.md",
}),
},
],
parameters: {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: {
action: {
type: "string",
enum: ["list", "summarize"],
description:
"The action to take. 'list' will return all files available with their filename and descriptions. 'summarize' will open and summarize the file by the a document name.",
},
document_filename: {
type: "string",
"x-nullable": true,
description:
"The file name of the document you want to get the full content of.",
},
},
additionalProperties: false,
},
handler: async function ({ action, document_filename }) {
if (action === "list") return await this.listDocuments();
if (action === "summarize")
return await this.summarizeDoc(document_filename);
return "There is nothing we can do. This function call returns no information.";
},
/**
* List all documents available in a workspace
* @returns List of files and their descriptions if available.
*/
listDocuments: async function () {
try {
this.super.introspect(
`${this.caller}: Looking at the available documents.`
);
const documents = await Document.where({
workspaceId: this.super.handlerProps.invocation.workspace_id,
});
if (documents.length === 0)
return "No documents found - nothing can be done. Stop.";
this.super.introspect(
`${this.caller}: Found ${documents.length} documents`
);
const foundDocuments = documents.map((doc) => {
const metadata = safeJsonParse(doc.metadata, {});
return {
document_id: doc.docId,
filename: metadata?.title ?? "unknown.txt",
description: metadata?.description ?? "no description",
};
});
return JSON.stringify(foundDocuments);
} catch (error) {
this.super.handlerProps.log(
`document-summarizer.list raised an error. ${error.message}`
);
return `Let the user know this action was not successful. An error was raised while listing available files. ${error.message}`;
}
},
summarizeDoc: async function (filename) {
try {
const availableDocs = safeJsonParse(
await this.listDocuments(),
[]
);
if (!availableDocs.length) {
this.super.handlerProps.log(
`${this.caller}: No available documents to summarize.`
);
return "No documents were found.";
}
const docInfo = availableDocs.find(
(info) => info.filename === filename
);
if (!docInfo) {
this.super.handlerProps.log(
`${this.caller}: No available document by the name "${filename}".`
);
return `No available document by the name "${filename}".`;
}
const document = await Document.content(docInfo.document_id);
this.super.introspect(
`${this.caller}: Grabbing all content for ${
filename ?? "a discovered file."
}`
);
if (!document.content || document.content.length === 0) {
throw new Error(
"This document has no readable content that could be found."
);
}
const { TokenManager } = require("../../../helpers/tiktoken");
if (
new TokenManager(this.super.model).countFromString(
document.content
) < Provider.contextLimit(this.super.provider, this.super.model)
) {
return document.content;
}
this.super.introspect(
`${this.caller}: Summarizing ${filename ?? ""}...`
);
this.super.onAbort(() => {
this.super.handlerProps.log(
"Abort was triggered, exiting summarization early."
);
this.controller.abort();
});
return await summarizeContent({
provider: this.super.provider,
model: this.super.model,
controllerSignal: this.controller.signal,
content: document.content,
});
} catch (error) {
this.super.handlerProps.log(
`document-summarizer.summarizeDoc raised an error. ${error.message}`
);
return `Let the user know this action was not successful. An error was raised while summarizing the file. ${error.message}`;
}
},
});
},
};
},
};
module.exports = {
docSummarizer,
};