aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/Agent.ts4
-rw-r--r--src/client/views/nodes/chatbot/tools/SortDocsTool.ts65
-rw-r--r--src/client/views/nodes/chatbot/tools/TagDocsTool.ts99
3 files changed, 166 insertions, 2 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
index ebcd2fa31..d1248d098 100644
--- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts
+++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
@@ -105,8 +105,8 @@ export class Agent {
codebaseSummarySearch: new CodebaseSummarySearchTool(this.vectorstore),
fileContent: new FileContentTool(this.vectorstore),
fileNames: new FileNamesTool(this.vectorstore),
- SortDocsTool: new SortDocsTool(this._docManager),
- TagDocsTool: new TagDocsTool(this._docManager),
+ sortDocs: new SortDocsTool(this._docManager),
+ tagDocs: new TagDocsTool(this._docManager),
};
// Add the createNewTool after other tools are defined
diff --git a/src/client/views/nodes/chatbot/tools/SortDocsTool.ts b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
new file mode 100644
index 000000000..e91a27b36
--- /dev/null
+++ b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
@@ -0,0 +1,65 @@
+import { BaseTool } from './BaseTool';
+import { Observation } from '../types/types';
+import { ParametersType, ToolInfo } from '../types/tool_types';
+import { AgentDocumentManager } from '../utils/AgentDocumentManager';
+import { gptAPICall, GPTCallType } from '../../../../apis/gpt/GPT';
+import { ChatSortField } from '../../../collections/CollectionSubView';
+import { v4 as uuidv4 } from 'uuid';
+
+const parameterRules = [
+ {
+ name: 'sortCriteria',
+ type: 'string',
+ description: 'Criteria provided by the user to sort the documents.',
+ required: true,
+ },
+] as const;
+
+const toolInfo: ToolInfo<typeof parameterRules> = {
+ name: 'sortDocs',
+ description:
+ 'Sorts documents within the current Dash environment based on user-specified criteria. Provide clear sorting criteria, such as by date, title, relevance, or custom metadata fields.',
+ parameterRules,
+ citationRules: 'No citation needed for sorting operations.',
+};
+
+export class SortDocsTool extends BaseTool<typeof parameterRules> {
+ private _docManager: AgentDocumentManager;
+
+ constructor(docManager: AgentDocumentManager) {
+ super(toolInfo);
+ this._docManager = docManager;
+ this._docManager.initializeFindDocsFreeform();
+ }
+
+ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+ try {
+ const docs = this._docManager.docIds.map((id) => this._docManager.extractDocumentMetadata(id));
+
+ const descriptions = docs
+ .filter((doc): doc is NonNullable<typeof doc> => doc !== null)
+ .map(
+ (doc) => `${doc.id}: ${doc.title} - ${doc.fields.layout.summary || ''}`
+ )
+ .join('\n');
+
+ const sortedIdsResponse = await gptAPICall(args.sortCriteria, GPTCallType.SORTDOCS, descriptions);
+ const sortedIds = sortedIdsResponse.trim().split('\n');
+
+ sortedIds.forEach((id, index) => {
+ this._docManager.editDocumentField(id, ChatSortField, index);
+ });
+
+ return [{
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="sort_status">Successfully sorted ${sortedIds.length} documents based on "${args.sortCriteria}".</chunk>`,
+ }];
+ } catch (error) {
+ return [{
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">Sorting failed: ${error instanceof Error ? error.message : String(error)}</chunk>`,
+ }];
+ }
+ }
+}
diff --git a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
new file mode 100644
index 000000000..2bd2059e6
--- /dev/null
+++ b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
@@ -0,0 +1,99 @@
+import { BaseTool } from './BaseTool';
+import { Observation } from '../types/types';
+import { ParametersType, ToolInfo } from '../types/tool_types';
+import { AgentDocumentManager } from '../utils/AgentDocumentManager';
+import { gptAPICall, GPTCallType } from '../../../../apis/gpt/GPT';
+import { v4 as uuidv4 } from 'uuid';
+import { List } from '../../../../../fields/List';
+import { StrListCast } from '../../../../../fields/Doc';
+const parameterRules = [
+ {
+ name: 'taggingCriteria',
+ type: 'string',
+ description: 'Criteria provided by the user to generate and apply tags to the documents.',
+ required: true,
+ },
+] as const;
+
+const toolInfo: ToolInfo<typeof parameterRules> = {
+ name: 'tagDocs',
+ description:
+ 'Generates and applies tags to documents within the Dash environment based on user-defined criteria, such as content, type, or custom metadata.',
+ parameterRules,
+ citationRules: 'No citation needed for tagging operations.',
+};
+
+export class TagDocsTool extends BaseTool<typeof parameterRules> {
+ private _docManager: AgentDocumentManager;
+
+ constructor(docManager: AgentDocumentManager) {
+ super(toolInfo);
+ this._docManager = docManager;
+ this._docManager.initializeFindDocsFreeform();
+ }
+
+ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+ try {
+ const docs = this._docManager.docIds.map((id) => this._docManager.extractDocumentMetadata(id));
+
+ const descriptions = docs
+ .filter((doc): doc is NonNullable<typeof doc> => doc !== null)
+ .map(
+ (doc) => `${doc.id}: ${doc.title} - ${doc.fields.layout.summary || ''}`
+ )
+ .join('\n');
+ const taggingResultsRaw = await gptAPICall(args.taggingCriteria, GPTCallType.TAGDOCS, descriptions);
+ console.log('<<TAG RAW>>', JSON.stringify(taggingResultsRaw));
+ const taggingResults = taggingResultsRaw.trim().split('\n');
+ const appliedTags: Record<string, string[]> = {};
+
+ for (const line of taggingResults) {
+ const [maybeId, maybeTags] = line.split(':');
+ if (!maybeId || !maybeTags) continue;
+
+ const docId = maybeId.trim();
+ const predictedTags = maybeTags
+ .split(',')
+ .map(t => t.trim())
+ .filter(t => t.length > 0)
+ .map(t => t.startsWith('#') ? t : `#${t}`);
+
+ // 1) Retrieve the actual Doc from your manager
+ const layoutDoc = this._docManager.getDocument(docId);
+ if (!layoutDoc) {
+ console.warn(`Doc ${docId} not found – skipping tags.`);
+ continue;
+ }
+
+ // 2) Ensure the chat‐tag list exists (fieldKey might vary)
+ // using a custom List field on the Doc called `$tags_chat`:
+ let tagList = layoutDoc.$tags_chat as List<string> | undefined;
+ if (!tagList) {
+ tagList = new List<string>();
+ layoutDoc.$tags_chat = tagList;
+ }
+
+ // 3) Push each predicted tag (the List removes duplicates automatically)
+ predictedTags.forEach(tag => tagList!.push(tag));
+
+ // 4) Accumulate for reporting
+ appliedTags[docId] = StrListCast(tagList)
+ }
+
+ const resultSummary = Object.entries(appliedTags)
+ .map(([id, tags]) => `${id}: ${tags.join(', ')}`)
+ .join('; ');
+
+ return [{
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="tagging_status">Successfully tagged documents based on "${args.taggingCriteria}". Tags applied: ${resultSummary}</chunk>`,
+ }];
+} catch (error) {
+ return [{
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">Tagging failed: ${error instanceof Error ? error.message : String(error)}</chunk>`,
+ }];
+}
+}
+}