diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/chatbot/agentsystem/Agent.ts | 4 | ||||
-rw-r--r-- | src/client/views/nodes/chatbot/tools/SortDocsTool.ts | 65 | ||||
-rw-r--r-- | src/client/views/nodes/chatbot/tools/TagDocsTool.ts | 99 |
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>`, + }]; +} +} +} |