diff options
author | sharkiecodes <lanyi_stroud@brown.edu> | 2025-07-22 13:44:08 -0400 |
---|---|---|
committer | sharkiecodes <lanyi_stroud@brown.edu> | 2025-07-22 13:44:08 -0400 |
commit | 8ff34d5335093c4ff85473227f39b3e83133d999 (patch) | |
tree | 39e295351356c81f88b6a028a70b9b812ab2a4f0 /src | |
parent | d31a740378e8d4fd58ec329ba83dd20d28bfe5b4 (diff) |
Completed agent functionality - recognition of all documents on canvas
Diffstat (limited to 'src')
6 files changed, 562 insertions, 179 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts index 47e2e8fd3..6a15d0c1d 100644 --- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts +++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts @@ -33,7 +33,8 @@ import { TakeQuizTool } from '../tools/TakeQuizTool'; import { GPTTutorialTool } from '../tools/TutorialTool'; import { WebsiteInfoScraperTool } from '../tools/WebsiteInfoScraperTool'; import { AgentDocumentManager } from '../utils/AgentDocumentManager'; -import { FilterDocsTool } from '../tools/FilterDocTool'; +import { FilterDocsTool } from '../tools/FilterDocsTool'; +import { CanvasDocsTool } from '../tools/CanvasDocsTool'; dotenv.config(); @@ -91,6 +92,7 @@ export class Agent { this._history = history; this._csvData = csvData; this._docManager = docManager; + this.is_dash_doc_assistant = true; // Initialize to default value // Initialize dynamic tool registry this.dynamicToolRegistry = new Map(); @@ -114,6 +116,7 @@ export class Agent { tagDocs: new TagDocsTool(this._docManager), filterDocs: new FilterDocsTool(this._docManager, this.parentView), takeQuiz: new TakeQuizTool(this._docManager), + canvasDocs: new CanvasDocsTool(), }; diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss index 0bacc70c2..f6d340360 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss @@ -58,6 +58,13 @@ $font-size-xlarge: 18px; text-align: center; } + .header-controls { + display: flex; + align-items: center; + gap: 8px; + } + + .canvas-mode-toggle, .font-size-control { display: flex; align-items: center; @@ -79,6 +86,32 @@ $font-size-xlarge: 18px; } } + .canvas-mode-toggle { + position: relative; + + // Visual indicator for active state + &::after { + content: ''; + position: absolute; + bottom: -2px; + left: 50%; + transform: translateX(-50%); + width: 4px; + height: 4px; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.7); + opacity: 0; + transition: opacity 0.2s ease; + } + + // Show indicator when canvas mode is active + &.canvas-active::after { + opacity: 1; + } + + + } + .font-size-modal { position: absolute; top: 100%; diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index 732c4d637..195e85412 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -49,6 +49,8 @@ import { AgentDocumentManager } from '../utils/AgentDocumentManager'; import { AiOutlineSend } from 'react-icons/ai'; import { SnappingManager } from '../../../../util/SnappingManager'; import { Button, Size, Type } from '@dash/components'; +import { MdLink, MdViewModule } from 'react-icons/md'; +import { Tooltip } from '@mui/material'; dotenv.config(); @@ -105,6 +107,12 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._inputValue = input; }); + @action + toggleCanvasMode = () => { + const newMode = !this.docManager.getCanvasMode(); + this.docManager.setCanvasMode(newMode); + }; + /** * Constructor initializes the component, sets up OpenAI, vector store, and agent instances, * and observes changes in the chat history to save the state in dataDoc. @@ -1438,8 +1446,25 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { )} <div className="chat-header"> <h2>{this.userName()}'s AI Assistant</h2> - <div className="font-size-control" onClick={this.toggleFontSizeModal}> - {this.renderFontSizeIcon()} + <div className="header-controls"> + <Tooltip title={ + <div className="dash-tooltip"> + {this.docManager.getCanvasMode() + ? "Click to limit scope to linked documents" + : "Click to expand scope to all documents on canvas" + } + </div> + } placement="bottom"> + <div + className={`canvas-mode-toggle ${this.docManager.getCanvasMode() ? 'canvas-active' : ''}`} + onClick={this.toggleCanvasMode} + > + {this.docManager.getCanvasMode() ? <MdViewModule /> : <MdLink />} + </div> + </Tooltip> + <div className="font-size-control" onClick={this.toggleFontSizeModal}> + {this.renderFontSizeIcon()} + </div> </div> {this._isFontSizeModalOpen && ( <div className="font-size-modal"> diff --git a/src/client/views/nodes/chatbot/tools/CanvasDocsTool.ts b/src/client/views/nodes/chatbot/tools/CanvasDocsTool.ts new file mode 100644 index 000000000..daf6ed941 --- /dev/null +++ b/src/client/views/nodes/chatbot/tools/CanvasDocsTool.ts @@ -0,0 +1,428 @@ +// CanvasDocsTool.ts - Provides access to all documents on the canvas +import { BaseTool } from './BaseTool'; +import { Observation } from '../types/types'; +import { ParametersType, ToolInfo } from '../types/tool_types'; +import { DocumentView } from '../../DocumentView'; +import { Doc } from '../../../../../fields/Doc'; +import { v4 as uuidv4 } from 'uuid'; +import { Id } from '../../../../../fields/FieldSymbols'; +import { DocumentType } from '../../../../documents/DocumentTypes'; + +const parameterRules = [ + { + name: 'action', + type: 'string', + description: 'The action to perform: "list" to get all canvas documents, "getById" to get a specific document, "getByType" to filter by document type', + required: true, + }, + { + name: 'documentId', + type: 'string', + description: 'The ID of a specific document to retrieve (required for "getById" action)', + required: false, + }, + { + name: 'documentType', + type: 'string', + description: 'Filter documents by type: text, image, pdf, video, audio, web, diagram, dataviz, collection, etc. (required for "getByType" action)', + required: false, + }, + { + name: 'includeSystemDocs', + type: 'boolean', + description: 'Whether to include system documents in the results (default: false)', + required: false, + }, +] as const; + +const toolInfo: ToolInfo<typeof parameterRules> = { + name: 'canvasDocs', + description: 'Access and analyze all documents currently visible on the canvas/workspace. This tool provides broader document recognition beyond just linked documents, allowing the agent to work with any document in the current view. Useful for comprehensive document analysis, finding related content, and understanding the full context of the workspace.', + parameterRules, + citationRules: 'Cite documents by their title and type when referencing canvas documents.', +}; + +export class CanvasDocsTool extends BaseTool<typeof parameterRules> { + + constructor() { + super(toolInfo); + } + + /** + * Get all documents currently visible on the canvas using parent view approach + */ + private getAllCanvasDocuments(includeSystemDocs: boolean = false): Doc[] { + try { + console.log('[CanvasDocsTool] Starting canvas document discovery...'); + + const canvasDocs: Doc[] = []; + const seenIds = new Set<string>(); + const sources = { parentView: 0, allViews: 0, lightbox: 0 }; + + // Method 1: Try to get documents from parent view if it's a collection + const parentViewDocs = this.getParentViewDocuments(); + if (parentViewDocs.length > 0) { + console.log(`[CanvasDocsTool] Found ${parentViewDocs.length} documents from parent view`); + const parentDocsAdded = parentViewDocs.reduce((count, doc) => { + return this.addDocumentToResults(doc, canvasDocs, seenIds, includeSystemDocs, 'parentView') ? count + 1 : count; + }, 0); + sources.parentView = parentDocsAdded; + } + + // Method 2: Get documents from all rendered views (fallback) + const viewDocs = this.getDocumentsFromViews(); + console.log(`[CanvasDocsTool] Found ${viewDocs.length} documents from document views`); + const viewDocsAdded = viewDocs.reduce((count, doc) => { + return this.addDocumentToResults(doc, canvasDocs, seenIds, includeSystemDocs, 'allViews') ? count + 1 : count; + }, 0); + sources.allViews = viewDocsAdded; + + // Method 3: Include lightbox document if active + const lightboxDoc = this.getLightboxDocument(); + if (lightboxDoc) { + console.log('[CanvasDocsTool] Found lightbox document'); + sources.lightbox = this.addDocumentToResults(lightboxDoc, canvasDocs, seenIds, includeSystemDocs, 'lightbox') ? 1 : 0; + } + + console.log(`[CanvasDocsTool] Total: ${canvasDocs.length} documents found from sources:`, sources); + return canvasDocs; + } catch (error) { + console.error('[CanvasDocsTool] Error getting canvas documents:', error); + return []; + } + } + + /** + * Get documents from the parent view if it's a collection + */ + private getParentViewDocuments(): Doc[] { + try { + // Get all DocumentViews and look for collections with child documents + const allViews = DocumentView.allViews(); + const parentDocs: Doc[] = []; + + for (const view of allViews) { + // Check if this view has a ComponentView with child documents + const componentView = (view as any).ComponentView || (view as any)._componentView; + + if (componentView) { + // Method 1: Try hasChildDocs() which returns layout documents + if (typeof componentView.hasChildDocs === 'function') { + try { + const childDocs = componentView.hasChildDocs(); + if (Array.isArray(childDocs) && childDocs.length > 0) { + console.log(`[CanvasDocsTool] Found collection with ${childDocs.length} child documents from hasChildDocs`); + childDocs.forEach((doc: any, index: number) => { + console.log(`[CanvasDocsTool] Child doc ${index}: id=${doc?.[Id]}, title="${doc?.title}", type=${typeof doc}`); + if (doc && typeof doc === 'object' && doc[Id]) { + parentDocs.push(doc); + } + }); + } + } catch (e) { + console.log('[CanvasDocsTool] Error calling hasChildDocs:', e); + } + } + + // Method 2: Try childLayoutPairs property + if (componentView.childLayoutPairs) { + try { + const childPairs = componentView.childLayoutPairs; + if (Array.isArray(childPairs)) { + console.log(`[CanvasDocsTool] Found ${childPairs.length} childLayoutPairs`); + childPairs.forEach((pair: any, index: number) => { + const layoutDoc = pair.layout; + console.log(`[CanvasDocsTool] Pair ${index}: layout id=${layoutDoc?.[Id]}, title="${layoutDoc?.title}"`); + if (layoutDoc && layoutDoc[Id]) { + parentDocs.push(layoutDoc); + } + }); + } + } catch (e) { + console.log('[CanvasDocsTool] Error accessing childLayoutPairs:', e); + } + } + + // Method 3: Try childDocs property (source data) + if (componentView.childDocs) { + try { + const childDocs = componentView.childDocs; + if (Array.isArray(childDocs)) { + console.log(`[CanvasDocsTool] Found ${childDocs.length} raw childDocs`); + childDocs.forEach((doc: any, index: number) => { + console.log(`[CanvasDocsTool] Raw doc ${index}: id=${doc?.[Id]}, title="${doc?.title}"`); + if (doc && doc[Id]) { + parentDocs.push(doc); + } + }); + } + } catch (e) { + console.log('[CanvasDocsTool] Error accessing childDocs:', e); + } + } + } + } + + console.log(`[CanvasDocsTool] Total parent docs collected: ${parentDocs.length}`); + return parentDocs; + } catch (error) { + console.error('[CanvasDocsTool] Error getting parent view documents:', error); + return []; + } + } + + /** + * Get documents from rendered DocumentViews + */ + private getDocumentsFromViews(): Doc[] { + try { + const allViews = DocumentView.allViews(); + const viewDocs: Doc[] = []; + + for (const view of allViews) { + const doc = view.Document; + if (doc && doc[Id]) { + viewDocs.push(doc); + } + } + + return viewDocs; + } catch (error) { + console.error('[CanvasDocsTool] Error getting view documents:', error); + return []; + } + } + + /** + * Get the current lightbox document if any + */ + private getLightboxDocument(): Doc | null { + try { + // Try to get lightbox document using DocumentView static method + if (typeof DocumentView.LightboxDoc === 'function') { + const lightboxDoc = DocumentView.LightboxDoc(); + return lightboxDoc || null; + } + return null; + } catch (error) { + console.log('[CanvasDocsTool] No lightbox document found'); + return null; + } + } + + /** + * Add a document to results if it passes filters + */ + private addDocumentToResults(doc: any, canvasDocs: Doc[], seenIds: Set<string>, includeSystemDocs: boolean, source: string): boolean { + if (!doc || !doc[Id]) { + console.log(`[CanvasDocsTool] Skipped document from ${source}: no doc or no ID`); + return false; + } + + const docId = String(doc[Id]); + if (seenIds.has(docId)) { + console.log(`[CanvasDocsTool] Skipped document from ${source}: duplicate ID ${docId}`); + return false; + } + seenIds.add(docId); + + // Skip system documents unless explicitly requested + if (!includeSystemDocs && Doc.IsSystem(doc)) { + console.log(`[CanvasDocsTool] Skipped document from ${source}: system document ${doc.title || docId}`); + return false; + } + + // Skip hidden documents + if (doc.hidden) { + console.log(`[CanvasDocsTool] Skipped document from ${source}: hidden document ${doc.title || docId}`); + return false; + } + + canvasDocs.push(doc); + console.log(`[CanvasDocsTool] Added document from ${source}: ${doc.title || 'Untitled'} (${doc.type || 'unknown'})`); + return true; + } + + /** + * Get document summary for display + */ + private getDocumentSummary(doc: Doc): any { + try { + return { + id: String(doc[Id] || 'unknown'), + title: String(doc.title || 'Untitled'), + type: doc.type || 'unknown', + x: doc.x || 0, + y: doc.y || 0, + width: doc._width || 'auto', + height: doc._height || 'auto', + tags: doc.tags ? Array.from(doc.tags as any) : [], + hasData: !!doc.data, + dataType: typeof doc.data, + lastModified: doc.lastModified || 'unknown', + layout: { + fitWidth: doc._layout_fitWidth || false, + autoHeight: doc._layout_autoHeight || false, + showTitle: doc._layout_showTitle || false, + } + }; + } catch (error) { + console.warn('[CanvasDocsTool] Error getting document summary for doc:', doc[Id], error); + return { + id: String(doc[Id] || 'unknown'), + title: 'Error reading document', + type: 'unknown', + error: String(error) + }; + } + } + + async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> { + const chunkId = uuidv4(); + console.log('[CanvasDocsTool] Executing action:', args.action); + + try { + const includeSystemDocs = args.includeSystemDocs || false; + const canvasDocs = this.getAllCanvasDocuments(includeSystemDocs); + + switch (args.action) { + case 'list': { + const documentSummaries = canvasDocs.map(doc => this.getDocumentSummary(doc)); + + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="canvas_documents"> +Found ${canvasDocs.length} documents on the canvas: + +${documentSummaries.map(doc => + `• ${doc.title} (${doc.type}) - ID: ${doc.id} - Position: (${doc.x}, ${doc.y}) - Size: ${doc.width}x${doc.height}` +).join('\n')} + +Document Details: +${JSON.stringify(documentSummaries, null, 2)} +</chunk>`, + }, + ]; + } + + case 'getById': { + if (!args.documentId) { + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="error"> +documentId parameter is required for getById action. +</chunk>`, + }, + ]; + } + + const targetDoc = canvasDocs.find(doc => String(doc[Id]) === args.documentId); + if (!targetDoc) { + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="error"> +Document with ID "${args.documentId}" not found on canvas. +Available document IDs: ${canvasDocs.map(d => d[Id]).join(', ')} +</chunk>`, + }, + ]; + } + + const docSummary = this.getDocumentSummary(targetDoc); + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="canvas_document"> +Document found: ${docSummary.title} (${docSummary.type}) + +${JSON.stringify(docSummary, null, 2)} +</chunk>`, + }, + ]; + } + + case 'getByType': { + if (!args.documentType) { + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="error"> +documentType parameter is required for getByType action. +Available types: ${Array.from(new Set(canvasDocs.map(d => d.type))).join(', ')} +</chunk>`, + }, + ]; + } + + const filteredDocs = canvasDocs.filter(doc => + doc.type === args.documentType || + (doc.type && typeof doc.type === 'string' && doc.type.toLowerCase() === args.documentType.toLowerCase()) + ); + + if (filteredDocs.length === 0) { + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="canvas_documents"> +No documents of type "${args.documentType}" found on canvas. +Available types: ${Array.from(new Set(canvasDocs.map(d => d.type))).join(', ')} +</chunk>`, + }, + ]; + } + + const documentSummaries = filteredDocs.map(doc => this.getDocumentSummary(doc)); + + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="canvas_documents"> +Found ${filteredDocs.length} documents of type "${args.documentType}" on canvas: + +${documentSummaries.map(doc => + `• ${doc.title} - ID: ${doc.id} - Position: (${doc.x}, ${doc.y})` +).join('\n')} + +Document Details: +${JSON.stringify(documentSummaries, null, 2)} +</chunk>`, + }, + ]; + } + + default: + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="error"> +Unknown action: "${args.action}". Available actions: list, getById, getByType +</chunk>`, + }, + ]; + } + + } catch (error) { + console.error('[CanvasDocsTool] Execution error:', error); + return [ + { + type: 'text', + text: `<chunk chunk_id="${chunkId}" chunk_type="error"> +Canvas documents access failed: ${error instanceof Error ? error.message : String(error)} +</chunk>`, + }, + ]; + } + } + + /** + * Get all canvas documents as a simple array (for external use) + */ + static getAllCanvasDocuments(includeSystemDocs: boolean = false): Doc[] { + // Create a temporary instance to use the private methods + const tool = new CanvasDocsTool(); + return tool.getAllCanvasDocuments(includeSystemDocs); + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/chatbot/tools/FilterDocTool.ts b/src/client/views/nodes/chatbot/tools/FilterDocTool.ts deleted file mode 100644 index 30c1dcc44..000000000 --- a/src/client/views/nodes/chatbot/tools/FilterDocTool.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* not in use// FilterDocsTool.ts -import { BaseTool } from './BaseTool'; -import { Observation } from '../types/types'; -import { ParametersType, ToolInfo } from '../types/tool_types'; -import { AgentDocumentManager } from '../utils/AgentDocumentManager'; -import { - gptAPICall, - GPTCallType, - DescriptionSeperator, - DataSeperator, -} from '../../../../apis/gpt/GPT'; -import { v4 as uuidv4 } from 'uuid'; -import { TagItem } from '../../../TagsView'; -import { DocumentView } from '../../DocumentView'; -import { Doc } from '../../../../../fields/Doc'; - -const parameterRules = [ - { - name: 'filterCriteria', - type: 'string', - description: 'Natural-language criteria for choosing a subset of documents.', - required: true, - }, -] as const; - -const toolInfo: ToolInfo<typeof parameterRules> = { - name: 'filterDocs', - description: 'Filters documents based on user-specified natural-language criteria.', - parameterRules, - citationRules: 'No citation needed for filtering operations.', -}; - -export class FilterDocsTool extends BaseTool<typeof parameterRules> { - private _docManager: AgentDocumentManager; - static ChatTag = '#chat'; // tag used by GPT popup to filter docs - private _collectionView: DocumentView; - - constructor(docManager: AgentDocumentManager, collectionView: DocumentView) { - super(toolInfo); - this._docManager = docManager; - this._docManager.initializeFindDocsFreeform(); - this._collectionView = collectionView; - } - - async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> { - const chunkId = uuidv4(); - console.log('[FilterDocsTool] execute() called with criteria:', args.filterCriteria); - - // 0) Check parent view & doc - const parentView = this._collectionView; - console.log('[FilterDocsTool] parentView:', parentView); - const parentDoc = parentView?.Document; - if (!parentDoc) { - console.error('[FilterDocsTool] Missing parentView.Document!'); - return [ - { - type: 'text', - text: `<chunk chunk_id="${chunkId}" chunk_type="error"> -FilterDocsTool: no parent DocumentView / parentDoc provided. -</chunk>`, - }, - ]; - } - console.log('[FilterDocsTool] parentDoc:', parentDoc.id || parentDoc); - - try { - // 1) Build description→ID map & prompt blocks - console.log('[FilterDocsTool] docIds:', this._docManager.docIds); - const textToId = new Map<string, string>(); - const blocks: string[] = []; - - for (const id of this._docManager.docIds) { - const descRaw = await this._docManager.getDocDescription(id); - const desc = descRaw.replace(/\n/g, ' ').trim(); - console.log(`[FilterDocsTool] got desc for ${id}:`, desc); - - if (!desc) { - console.warn(`[FilterDocsTool] skipping empty desc for ${id}`); - continue; - } - textToId.set(desc, id); - - const block = `${DescriptionSeperator}${desc}${DescriptionSeperator}`; - console.log('[FilterDocsTool] adding block:', block); - blocks.push(block); - } - - const prompt = blocks.join(''); - console.log('[FilterDocsTool] final prompt to GPT:', prompt); - - // 2) Ask GPT for subset - const raw = await gptAPICall(args.filterCriteria, GPTCallType.SUBSETDOCS, prompt); - console.log('[FilterDocsTool] GPT raw response:', raw); - - // 3) Clear existing chat-filter tags/filters - const allDocs = this._docManager.docIds - .map((id) => this._docManager.getDocument(id)) - .filter((d): d is Doc => !!d); - console.log('[FilterDocsTool] clearing existing tags from docs:', allDocs.map((d) => d.id || d)); - allDocs.forEach((d) => TagItem.removeTagFromDoc(d, FilterDocsTool.ChatTag)); - - console.log( - `[FilterDocsTool] removing docFilter('${FilterDocsTool.ChatTag}') from parentDoc ${parentDoc.id}` - ); - Doc.setDocFilter(parentDoc, 'tags', FilterDocsTool.ChatTag, 'remove'); - - // 4) Parse GPT’s output, re-apply tag + docFilter - const newlyTagged: string[] = []; - - raw - .split(DescriptionSeperator) - .filter((blk) => blk.trim() !== '') - .map((blk) => blk.replace(/\n/g, ' ').trim()) - .forEach((blk, idx) => { - console.log(`[FilterDocsTool] parsing block[${idx}]:`, blk); - const [descText = '', _extra] = blk.split(DataSeperator).map((s) => s.trim()); - console.log(` → descText = "${descText}", ignoring extra="${_extra}"`); - - if (!descText) { - console.warn('[FilterDocsTool] skipping block with empty descText:', blk); - return; - } - - const docId = textToId.get(descText); - if (!docId) { - console.warn('[FilterDocsTool] no match in textToId for descText:', descText); - return; - } - - const doc = this._docManager.getDocument(docId); - if (!doc) { - console.warn('[FilterDocsTool] no Doc instance for ID:', docId); - return; - } - - // apply the special '#chat' tag - console.log(`[FilterDocsTool] adding tag "${FilterDocsTool.ChatTag}" to doc ${docId}`); - TagItem.addTagToDoc(doc, FilterDocsTool.ChatTag); - newlyTagged.push(docId); - }); - - console.log('[FilterDocsTool] newly tagged IDs:', newlyTagged); - - // 5) Finally, set the parent’s filter to **check** on that tag - console.log( - `[FilterDocsTool] setting docFilter('${FilterDocsTool.ChatTag}', 'check') on parentDoc ${parentDoc.id}` - ); - Doc.setDocFilter(parentDoc, 'tags', FilterDocsTool.ChatTag, 'check'); - - // build summary - const summary = newlyTagged.length - ? newlyTagged.join(', ') - : '(none)'; - - return [ - { - type: 'text', - text: `<chunk chunk_id="${chunkId}" chunk_type="filter_status"> -Filtered documents based on "${args.filterCriteria}". Docs tagged ${FilterDocsTool.ChatTag}: ${summary} -</chunk>`, - }, - ]; - } catch (err) { - console.error('[FilterDocsTool] error', err); - return [ - { - type: 'text', - text: `<chunk chunk_id="${chunkId}" chunk_type="error"> -Filtering failed: ${err instanceof Error ? err.message : String(err)} -</chunk>`, - }, - ]; - } - } -} -*/
\ No newline at end of file diff --git a/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts b/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts index 088891022..a96d93a25 100644 --- a/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts +++ b/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts @@ -10,6 +10,7 @@ import { DocumentManager } from '../../../../util/DocumentManager'; import { LinkManager, UPDATE_SERVER_CACHE } from '../../../../util/LinkManager'; import { DocumentView } from '../../DocumentView'; import { ChatBox, parsedDoc } from '../chatboxcomponents/ChatBox'; +import { CanvasDocsTool } from '../tools/CanvasDocsTool'; import { supportedDocTypes } from '../types/tool_types'; import { CHUNK_TYPE, RAGChunk, SimplifiedChunk } from '../types/types'; @@ -29,6 +30,7 @@ export class AgentDocumentManager { private chatBox: ChatBox; private parentView: DocumentView; private chatBoxDocument: Doc | null = null; + @observable private _useCanvasMode: boolean = false; private fieldMetadata: Record<string, any> = {}; // bcz: CHANGE any to a proper type! @observable private simplifiedChunks: ObservableMap<string, SimplifiedChunk>; @@ -133,6 +135,74 @@ export class AgentDocumentManager { } /** + * Toggle between linked documents mode and canvas mode + */ + @action + public setCanvasMode(useCanvas: boolean) { + this._useCanvasMode = useCanvas; + console.log(`[AgentDocumentManager] Canvas mode ${useCanvas ? 'enabled' : 'disabled'}`); + + // Reinitialize documents based on new mode + if (useCanvas) { + this.initializeCanvasDocuments(); + } else { + this.initializeFindDocsFreeform(); + } + } + + /** + * Get current canvas mode status + */ + public get useCanvasMode(): boolean { + return this._useCanvasMode; + } + + /** + * Get current canvas mode status (for external access) + */ + public getCanvasMode(): boolean { + return this._useCanvasMode; + } + + /** + * Initialize documents from the entire canvas + */ + @action + public initializeCanvasDocuments() { + try { + console.log('[AgentDocumentManager] Finding all documents on canvas...'); + console.log('[AgentDocumentManager] Canvas mode enabled, looking for all documents...'); + + // Get all canvas documents using CanvasDocsTool + const canvasDocs = CanvasDocsTool.getAllCanvasDocuments(false); + console.log(`[AgentDocumentManager] Found ${canvasDocs.length} documents on canvas`); + + if (canvasDocs.length === 0) { + console.warn('[AgentDocumentManager] No documents found on canvas. This might indicate:'); + console.warn(' 1. No documents are currently rendered/visible'); + console.warn(' 2. All documents are considered "system" documents'); + console.warn(' 3. DocumentView.allViews() is not returning expected results'); + + // Let's also try including system docs to see if that's the issue + const canvasDocsWithSystem = CanvasDocsTool.getAllCanvasDocuments(true); + console.log(`[AgentDocumentManager] With system docs included: ${canvasDocsWithSystem.length} documents`); + } + + // Process each canvas document + canvasDocs.forEach(async (doc: Doc) => { + if (doc && doc !== this.chatBoxDocument) { // Don't process the chatbox itself + console.log('[AgentDocumentManager] Processing canvas document:', doc.id, doc.title, doc.type); + await this.processDocument(doc); + console.log('[AgentDocumentManager] Completed processing document:', doc.id); + } + }); + + } catch (error) { + console.error('[AgentDocumentManager] Error finding documents on canvas:', error); + } + } + + /** * Gets all documents in the same Freeform view as the ChatBox * Uses the LinkManager to get all linked documents, similar to how ChatBox does it */ |