aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts')
-rw-r--r--src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts884
1 files changed, 27 insertions, 857 deletions
diff --git a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
index 08351143b..4b751acc0 100644
--- a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
+++ b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
@@ -10,6 +10,7 @@ import { LinkManager } from '../../../../util/LinkManager';
import { DocCast, StrCast } from '../../../../../fields/Types';
import { supportedDocTypes } from '../types/tool_types';
import { parsedDoc } from '../chatboxcomponents/ChatBox';
+import { AgentDocumentManager } from '../utils/AgentDocumentManager';
// Define the parameters for the DocumentMetadataTool
const parameterDefinitions: ReadonlyArray<Parameter> = [
@@ -199,746 +200,12 @@ const documentMetadataToolInfo: ToolInfo<DocumentMetadataToolParamsType> = {
* and allows for editing document fields in the correct location.
*/
export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsType> {
- private freeformView: any;
- private chatBox: any;
- private chatBoxDocument: Doc | null = null;
- private documentsById: Map<string, Doc> = new Map();
- private layoutDocsById: Map<string, Doc> = new Map();
- private dataDocsById: Map<string, Doc> = new Map();
- private fieldMetadata: Record<string, any> = {};
- private readonly DOCUMENT_ID_FIELD = '_dash_document_id';
-
- constructor(chatBox: any) {
- super(documentMetadataToolInfo);
- this.chatBox = chatBox;
-
- // Store a direct reference to the ChatBox document
- if (chatBox && chatBox.Document) {
- this.chatBoxDocument = chatBox.Document;
- if (this.chatBoxDocument && this.chatBoxDocument.id) {
- console.log('DocumentMetadataTool initialized with ChatBox Document:', this.chatBoxDocument.id);
- } else {
- console.log('DocumentMetadataTool initialized with ChatBox Document (no ID)');
- }
- } else if (chatBox && chatBox.props && chatBox.props.Document) {
- this.chatBoxDocument = chatBox.props.Document;
- if (this.chatBoxDocument && this.chatBoxDocument.id) {
- console.log('DocumentMetadataTool initialized with ChatBox props.Document:', this.chatBoxDocument.id);
- } else {
- console.log('DocumentMetadataTool initialized with ChatBox props.Document (no ID)');
- }
- } else {
- console.warn('DocumentMetadataTool initialized without valid ChatBox Document reference');
- }
-
- this.initializeFieldMetadata();
- }
-
- /**
- * Extracts field metadata from DocumentOptions class
- */
- private initializeFieldMetadata() {
- // Parse DocumentOptions to extract field definitions
- const documentOptionsInstance = new DocumentOptions();
- const documentOptionsEntries = Object.entries(documentOptionsInstance);
-
- for (const [fieldName, fieldInfo] of documentOptionsEntries) {
- // Extract field information
- const fieldData: Record<string, any> = {
- name: fieldName,
- withoutUnderscore: fieldName.startsWith('_') ? fieldName.substring(1) : fieldName,
- description: '',
- type: 'unknown',
- required: false,
- defaultValue: undefined,
- possibleValues: [],
- };
-
- // Check if fieldInfo has description property (it's likely a FInfo instance)
- if (fieldInfo && typeof fieldInfo === 'object' && 'description' in fieldInfo) {
- fieldData.description = fieldInfo.description;
-
- // Extract field type if available
- if ('fieldType' in fieldInfo) {
- fieldData.type = fieldInfo.fieldType;
- }
-
- // Extract possible values if available
- if ('values' in fieldInfo && Array.isArray(fieldInfo.values)) {
- fieldData.possibleValues = fieldInfo.values;
- }
- }
-
- this.fieldMetadata[fieldName] = fieldData;
- }
- }
-
- /**
- * 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
- */
- private findDocumentsInFreeformView() {
- // Reset collections
- this.documentsById.clear();
- this.layoutDocsById.clear();
- this.dataDocsById.clear();
-
- try {
- // Use the LinkManager approach which is proven to work in ChatBox
- if (this.chatBoxDocument) {
- console.log('Finding documents linked to ChatBox document with ID:', this.chatBoxDocument.id);
-
- // Get directly linked documents via LinkManager
- const linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.chatBoxDocument)
- .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.chatBoxDocument!)))
- .map(d => DocCast(d?.annotationOn, d))
- .filter(d => d);
-
- console.log(`Found ${linkedDocs.length} linked documents via LinkManager`);
-
- // Process the linked documents
- linkedDocs.forEach((doc: Doc) => {
- if (doc) {
- this.processDocument(doc);
- }
- });
-
- // Include the ChatBox document itself
- this.processDocument(this.chatBoxDocument);
-
- // If we have access to the Document's parent, try to find sibling documents
- if (this.chatBoxDocument.parent) {
- const parent = this.chatBoxDocument.parent;
- console.log('Found parent document, checking for siblings');
-
- // Check if parent is a Doc type and has a childDocs function
- if (parent && typeof parent === 'object' && 'childDocs' in parent && typeof parent.childDocs === 'function') {
- try {
- const siblingDocs = parent.childDocs();
- if (Array.isArray(siblingDocs)) {
- console.log(`Found ${siblingDocs.length} sibling documents via parent.childDocs()`);
- siblingDocs.forEach((doc: Doc) => {
- if (doc) {
- this.processDocument(doc);
- }
- });
- }
- } catch (e) {
- console.warn('Error accessing parent.childDocs:', e);
- }
- }
- }
- } else if (this.chatBox && this.chatBox.linkedDocs) {
- // If we have direct access to the linkedDocs computed property from ChatBox
- console.log('Using ChatBox.linkedDocs directly');
- const linkedDocs = this.chatBox.linkedDocs;
- if (Array.isArray(linkedDocs)) {
- console.log(`Found ${linkedDocs.length} documents via ChatBox.linkedDocs`);
- linkedDocs.forEach((doc: Doc) => {
- if (doc) {
- this.processDocument(doc);
- }
- });
- }
-
- // Process the ChatBox document if available
- if (this.chatBox.Document) {
- this.processDocument(this.chatBox.Document);
- }
- } else {
- console.warn('No ChatBox document reference available for finding linked documents');
- }
-
- console.log(`DocumentMetadataTool found ${this.documentsById.size} total documents`);
-
- // If we didn't find any documents, try a fallback method
- if (this.documentsById.size === 0 && this.chatBox) {
- console.log('No documents found, trying fallback method');
-
- // Try to access any field that might contain documents
- if (this.chatBox.props && this.chatBox.props.documents) {
- const documents = this.chatBox.props.documents;
- if (Array.isArray(documents)) {
- console.log(`Found ${documents.length} documents via ChatBox.props.documents`);
- documents.forEach((doc: Doc) => {
- if (doc) {
- this.processDocument(doc);
- }
- });
- }
- }
- }
- } catch (error) {
- console.error('Error finding documents in Freeform view:', error);
- }
- }
-
- /**
- * Process a document by ensuring it has an ID and adding it to the appropriate collections
- * @param doc The document to process
- */
- private processDocument(doc: Doc) {
- // Ensure document has a persistent ID
- const docId = this.ensureDocumentId(doc);
-
- // Only add if we haven't already processed this document
- if (!this.documentsById.has(docId)) {
- this.documentsById.set(docId, doc);
-
- // Get layout doc (the document itself or its layout)
- // TODO: Check if this works.
- const layoutDoc = doc;
- if (layoutDoc) {
- this.layoutDocsById.set(docId, layoutDoc);
- }
-
- // Get data doc
- const dataDoc = doc[DocData];
- if (dataDoc) {
- this.dataDocsById.set(docId, dataDoc);
- }
- }
- }
-
- /**
- * Ensures a document has a persistent ID stored in its metadata
- * @param doc The document to ensure has an ID
- * @returns The document's ID
- */
- private ensureDocumentId(doc: Doc): string {
- let docId: string | undefined;
-
- // First try to get the ID from our custom field
- if (doc[this.DOCUMENT_ID_FIELD]) {
- docId = String(doc[this.DOCUMENT_ID_FIELD]);
- return docId;
- }
-
- // Try different ways to get a document ID
-
- // 1. Try the direct id property if it exists
- if (doc.id && typeof doc.id === 'string') {
- docId = doc.id;
- }
- // 2. Try doc._id if it exists
- else if (doc._id && typeof doc._id === 'string') {
- docId = doc._id;
- }
- // 3. Try doc.data?.id if it exists
- else if (doc.data && typeof doc.data === 'object' && 'id' in doc.data && typeof doc.data.id === 'string') {
- docId = doc.data.id;
- }
- // 4. If none of the above work, generate a UUID
- else {
- docId = uuidv4();
- console.log(`Generated new UUID for document with title: ${doc.title || 'Untitled'}`);
- }
-
- // Store the ID in the document's metadata so it persists
- try {
- doc[this.DOCUMENT_ID_FIELD] = docId;
- } catch (e) {
- console.warn(`Could not assign ID to document property`, e);
- }
-
- return docId;
- }
-
- /**
- * Extracts metadata from a specific document
- * @param docId The ID of the document to extract metadata from
- * @returns An object containing the document's metadata
- */
- private extractDocumentMetadata(doc?: Doc) {
- if (!doc) return null;
- const layoutDoc = doc;
- const dataDoc = doc[DocData];
-
- const metadata: Record<string, any> = {
- id: doc.dash_document_id || doc.id || '',
- title: doc.title || '',
- type: doc.type || '',
- fields: {
- layout: {},
- data: {},
- },
- fieldLocationMap: {},
- };
-
- // Process all known field definitions
- Object.keys(this.fieldMetadata).forEach(fieldName => {
- const fieldDef = this.fieldMetadata[fieldName];
- const strippedName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName;
-
- // Check if field exists on layout document
- let layoutValue = undefined;
- if (layoutDoc) {
- layoutValue = layoutDoc[fieldName];
- if (layoutValue !== undefined) {
- // Field exists on layout document
- metadata.fields.layout[fieldName] = this.formatFieldValue(layoutValue);
- metadata.fieldLocationMap[strippedName] = 'layout';
- }
- }
-
- // Check if field exists on data document
- let dataValue = undefined;
- if (dataDoc) {
- dataValue = dataDoc[fieldName];
- if (dataValue !== undefined) {
- // Field exists on data document
- metadata.fields.data[fieldName] = this.formatFieldValue(dataValue);
- if (!metadata.fieldLocationMap[strippedName]) {
- metadata.fieldLocationMap[strippedName] = 'data';
- }
- }
- }
-
- // For fields with stripped names (without leading underscore),
- // also check if they exist on documents without the underscore
- if (fieldName.startsWith('_')) {
- const nonUnderscoreFieldName = fieldName.substring(1);
-
- if (layoutDoc) {
- const nonUnderscoreLayoutValue = layoutDoc[nonUnderscoreFieldName];
- if (nonUnderscoreLayoutValue !== undefined) {
- metadata.fields.layout[nonUnderscoreFieldName] = this.formatFieldValue(nonUnderscoreLayoutValue);
- metadata.fieldLocationMap[nonUnderscoreFieldName] = 'layout';
- }
- }
-
- if (dataDoc) {
- const nonUnderscoreDataValue = dataDoc[nonUnderscoreFieldName];
- if (nonUnderscoreDataValue !== undefined) {
- metadata.fields.data[nonUnderscoreFieldName] = this.formatFieldValue(nonUnderscoreDataValue);
- if (!metadata.fieldLocationMap[nonUnderscoreFieldName]) {
- metadata.fieldLocationMap[nonUnderscoreFieldName] = 'data';
- }
- }
- }
- }
- });
-
- // Add common field aliases for easier discovery
- // This helps users understand both width and _width refer to the same property
- if (metadata.fields.layout._width !== undefined && metadata.fields.layout.width === undefined) {
- metadata.fields.layout.width = metadata.fields.layout._width;
- metadata.fieldLocationMap.width = 'layout';
- }
-
- if (metadata.fields.layout._height !== undefined && metadata.fields.layout.height === undefined) {
- metadata.fields.layout.height = metadata.fields.layout._height;
- metadata.fieldLocationMap.height = 'layout';
- }
-
- return metadata;
- }
+ private _docManager: AgentDocumentManager;
- /**
- * Edits a specific field on a document
- * @param docId The ID of the document to edit
- * @param fieldName The name of the field to edit
- * @param fieldValue The new value for the field (string, number, or boolean)
- * @returns Object with success status, message, and additional information
- */
- private editDocumentField(
- docId: string,
- fieldName: string,
- fieldValue: string | number | boolean
- ): {
- success: boolean;
- message: string;
- fieldName?: string;
- originalFieldName?: string;
- newValue?: any;
- warning?: string;
- } {
- // Normalize field name (handle with/without underscore)
- let normalizedFieldName = fieldName.startsWith('_') ? fieldName : fieldName;
- const strippedFieldName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName;
-
- // Handle common field name aliases (width → _width, height → _height)
- // Many document fields use '_' prefix for layout properties
- if (fieldName === 'width') {
- normalizedFieldName = '_width';
- } else if (fieldName === 'height') {
- normalizedFieldName = '_height';
- }
-
- // Get the documents
- const doc = this.documentsById.get(docId);
- if (!doc) {
- return { success: false, message: `Document with ID ${docId} not found` };
- }
-
- const layoutDoc = this.layoutDocsById.get(docId);
- const dataDoc = this.dataDocsById.get(docId);
-
- if (!layoutDoc && !dataDoc) {
- return { success: false, message: `Could not find layout or data document for document with ID ${docId}` };
- }
-
- try {
- // Convert the field value to the appropriate type based on field metadata
- const convertedValue = this.convertFieldValue(normalizedFieldName, fieldValue);
-
- let targetDoc: Doc | undefined;
- let targetLocation: string;
-
- // First, check if field exists on layout document using Doc.Get
- if (layoutDoc) {
- const fieldExistsOnLayout = Doc.Get(layoutDoc, normalizedFieldName, true) !== undefined;
-
- // If it exists on layout document, update it there
- if (fieldExistsOnLayout) {
- targetDoc = layoutDoc;
- targetLocation = 'layout';
- }
- // If it has an underscore prefix, it's likely a layout property even if not yet set
- else if (normalizedFieldName.startsWith('_')) {
- targetDoc = layoutDoc;
- targetLocation = 'layout';
- }
- // Otherwise, look for or create on data document
- else if (dataDoc) {
- targetDoc = dataDoc;
- targetLocation = 'data';
- }
- // If no data document available, default to layout
- else {
- targetDoc = layoutDoc;
- targetLocation = 'layout';
- }
- }
- // If no layout document, use data document
- else if (dataDoc) {
- targetDoc = dataDoc;
- targetLocation = 'data';
- } else {
- return { success: false, message: `No valid document found for editing` };
- }
-
- if (!targetDoc) {
- return { success: false, message: `Target document not available` };
- }
-
- // Set the field value on the target document
- targetDoc[normalizedFieldName] = convertedValue;
-
- return {
- success: true,
- message: `Successfully updated field '${normalizedFieldName}' on ${targetLocation} document (ID: ${docId})`,
- fieldName: normalizedFieldName,
- originalFieldName: fieldName,
- newValue: convertedValue,
- };
- } catch (error) {
- console.error('Error editing document field:', error);
- return {
- success: false,
- message: `Error updating field: ${error instanceof Error ? error.message : String(error)}`,
- };
- }
- }
-
- /**
- * Converts a string field value to the appropriate type based on field metadata
- * @param fieldName The name of the field
- * @param fieldValue The string value to convert
- * @returns The converted value with the appropriate type
- */
- private convertFieldValue(fieldName: string, fieldValue: any): any {
- // If fieldValue is already a number or boolean, we don't need to convert it from string
- if (typeof fieldValue === 'number' || typeof fieldValue === 'boolean') {
- return fieldValue;
- }
-
- // If fieldValue is a string "true" or "false", convert to boolean
- if (typeof fieldValue === 'string') {
- if (fieldValue.toLowerCase() === 'true') {
- return true;
- }
- if (fieldValue.toLowerCase() === 'false') {
- return false;
- }
- }
-
- // If fieldValue is not a string (and not a number or boolean), convert it to string
- if (typeof fieldValue !== 'string') {
- fieldValue = String(fieldValue);
- }
-
- // Special handling for text field - convert to proper RichTextField format
- if (fieldName === 'text') {
- try {
- // Check if it's already a valid JSON RichTextField
- JSON.parse(fieldValue);
- return fieldValue;
- } catch (e) {
- // It's a plain text string, so convert it to RichTextField format
- const rtf = {
- doc: {
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- content: [
- {
- type: 'text',
- text: fieldValue,
- },
- ],
- },
- ],
- },
- };
- return JSON.stringify(rtf);
- }
- }
-
- // Get field metadata
- const normalizedFieldName = fieldName.startsWith('_') ? fieldName : `_${fieldName}`;
- const strippedFieldName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName;
-
- // Check both versions of the field name in metadata
- const fieldMeta = this.fieldMetadata[normalizedFieldName] || this.fieldMetadata[strippedFieldName];
-
- // Special handling for width and height without metadata
- if (!fieldMeta && (fieldName === '_width' || fieldName === '_height' || fieldName === 'width' || fieldName === 'height')) {
- const num = Number(fieldValue);
- return isNaN(num) ? fieldValue : num;
- }
-
- if (!fieldMeta) {
- // If no metadata found, just return the string value
- return fieldValue;
- }
-
- // Convert based on field type
- const fieldType = fieldMeta.type;
-
- if (fieldType === 'boolean') {
- // Convert to boolean
- return fieldValue.toLowerCase() === 'true';
- } else if (fieldType === 'number') {
- // Convert to number
- const num = Number(fieldValue);
- return isNaN(num) ? fieldValue : num;
- } else if (fieldType === 'date') {
- // Try to convert to date (stored as number timestamp)
- try {
- return new Date(fieldValue).getTime();
- } catch (e) {
- return fieldValue;
- }
- } else if (fieldType.includes('list') || fieldType.includes('array')) {
- // Try to parse as JSON array
- try {
- return JSON.parse(fieldValue);
- } catch (e) {
- return fieldValue;
- }
- } else if (fieldType === 'json' || fieldType === 'object') {
- // Try to parse as JSON object
- try {
- return JSON.parse(fieldValue);
- } catch (e) {
- return fieldValue;
- }
- }
-
- // Default to string
- return fieldValue;
- }
-
- /**
- * Formats a field value for JSON output
- * @param value The field value to format
- * @returns A JSON-friendly representation of the field value
- */
- private formatFieldValue(value: any): any {
- if (value === undefined || value === null) {
- return null;
- }
-
- // Handle Doc objects
- if (value instanceof Doc) {
- return {
- type: 'Doc',
- id: value.id || this.ensureDocumentId(value),
- title: value.title || '',
- docType: value.type || '',
- };
- }
-
- // Handle RichTextField (try to extract plain text)
- if (typeof value === 'string' && value.includes('"type":"doc"') && value.includes('"content":')) {
- try {
- const rtfObj = JSON.parse(value);
- // If this looks like a rich text field structure
- if (rtfObj.doc && rtfObj.doc.content) {
- // Recursively extract text from the content
- let plainText = '';
- const extractText = (node: any) => {
- if (node.text) {
- plainText += node.text;
- }
- if (node.content && Array.isArray(node.content)) {
- node.content.forEach((child: any) => extractText(child));
- }
- };
-
- extractText(rtfObj.doc);
-
- // If we successfully extracted text, show it, but also preserve the original value
- if (plainText) {
- return {
- type: 'RichText',
- text: plainText,
- length: plainText.length,
- // Don't include the full value as it can be very large
- };
- }
- }
- } catch (e) {
- // If parsing fails, just treat as a regular string
- }
- }
-
- // Handle arrays and complex objects
- if (typeof value === 'object') {
- // If the object has a toString method, use it
- if (value.toString && value.toString !== Object.prototype.toString) {
- return value.toString();
- }
-
- try {
- // Try to convert to JSON string
- return JSON.stringify(value);
- } catch (e) {
- return '[Complex Object]';
- }
- }
-
- // Return primitive values as is
- return value;
- }
-
- /**
- * Extracts all field metadata from DocumentOptions
- * @returns A structured object containing metadata about all available document fields
- */
- private getAllFieldMetadata() {
- // Start with our already populated fieldMetadata from the DocumentOptions class
- const result: Record<string, any> = {
- fieldCount: Object.keys(this.fieldMetadata).length,
- fields: {},
- fieldsByType: {
- string: [],
- number: [],
- boolean: [],
- //doc: [],
- //list: [],
- //date: [],
- //enumeration: [],
- //other: [],
- },
- fieldNameMappings: {},
- commonFields: {
- appearance: [],
- position: [],
- size: [],
- content: [],
- behavior: [],
- layout: [],
- },
- };
-
- // Process each field in the metadata
- Object.entries(this.fieldMetadata).forEach(([fieldName, fieldInfo]) => {
- const strippedName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName;
-
- // Add to fieldNameMappings
- if (fieldName.startsWith('_')) {
- result.fieldNameMappings[strippedName] = fieldName;
- }
-
- // Create structured field metadata
- const fieldData: Record<string, any> = {
- name: fieldName,
- displayName: strippedName,
- description: fieldInfo.description || '',
- type: fieldInfo.fieldType || 'unknown',
- possibleValues: fieldInfo.values || [],
- };
-
- // Add field to fields collection
- result.fields[fieldName] = fieldData;
-
- // Categorize by field type
- const type = fieldInfo.fieldType?.toLowerCase() || 'unknown';
- if (type === 'string') {
- result.fieldsByType.string.push(fieldName);
- } else if (type === 'number') {
- result.fieldsByType.number.push(fieldName);
- } else if (type === 'boolean') {
- result.fieldsByType.boolean.push(fieldName);
- } else if (type === 'doc') {
- //result.fieldsByType.doc.push(fieldName);
- } else if (type === 'list') {
- //result.fieldsByType.list.push(fieldName);
- } else if (type === 'date') {
- //result.fieldsByType.date.push(fieldName);
- } else if (type === 'enumeration') {
- //result.fieldsByType.enumeration.push(fieldName);
- } else {
- //result.fieldsByType.other.push(fieldName);
- }
-
- // Categorize by field purpose
- if (fieldName.includes('width') || fieldName.includes('height') || fieldName.includes('size')) {
- result.commonFields.size.push(fieldName);
- } else if (fieldName.includes('color') || fieldName.includes('background') || fieldName.includes('border')) {
- result.commonFields.appearance.push(fieldName);
- } else if (fieldName.includes('x') || fieldName.includes('y') || fieldName.includes('position') || fieldName.includes('pan')) {
- result.commonFields.position.push(fieldName);
- } else if (fieldName.includes('text') || fieldName.includes('title') || fieldName.includes('data')) {
- result.commonFields.content.push(fieldName);
- } else if (fieldName.includes('action') || fieldName.includes('click') || fieldName.includes('event')) {
- result.commonFields.behavior.push(fieldName);
- } else if (fieldName.includes('layout')) {
- result.commonFields.layout.push(fieldName);
- }
- });
-
- // Add special section for auto-sizing related fields
- result.autoSizingFields = {
- height: {
- autoHeightField: '_layout_autoHeight',
- heightField: '_height',
- displayName: 'height',
- usage: 'To manually set height, first set layout_autoHeight to false',
- },
- width: {
- autoWidthField: '_layout_autoWidth',
- widthField: '_width',
- displayName: 'width',
- usage: 'To manually set width, first set layout_autoWidth to false',
- },
- };
-
- // Add special section for text field format
- result.specialFields = {
- text: {
- name: 'text',
- description: 'Document text content',
- format: 'RichTextField',
- note: 'When setting text, provide plain text - it will be automatically converted to the correct format',
- example: 'For setting: "Hello world" (plain text); For getting: Will be converted to plaintext for display',
- },
- };
-
- return result;
+ constructor(docManager: AgentDocumentManager) {
+ super(documentMetadataToolInfo);
+ this._docManager = docManager;
+ this._docManager.initializeFindDocsFreeform();
}
/**
@@ -950,7 +217,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
console.log('DocumentMetadataTool: Executing with args:', args);
// Find all documents in the Freeform view
- this.findDocumentsInFreeformView();
+ this._docManager.initializeFindDocsFreeform();
try {
// Validate required input parameters based on action
@@ -981,7 +248,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
switch (action) {
case 'get': {
// Get metadata for a specific document or all documents
- const result = this.getDocumentMetadata(documentId);
+ const result = this._docManager.getDocumentMetadata(documentId);
console.log('DocumentMetadataTool: Get metadata result:', result);
return [
{
@@ -1003,7 +270,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
// Ensure document exists
- if (!this.documentsById.has(documentId)) {
+ if (!this._docManager.has(documentId)) {
return [
{
type: 'text',
@@ -1059,7 +326,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
const fieldName = String(edit.fieldName);
// Edit the field
- const result = this.editDocumentField(documentId, fieldName, fieldValue);
+ const result = this._docManager.editDocumentField(documentId, fieldName, fieldValue);
console.log(`DocumentMetadataTool: Edit field result for ${fieldName}:`, result);
@@ -1101,7 +368,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
// Get the updated metadata to return
- const updatedMetadata = this.getDocumentMetadata(documentId);
+ const updatedMetadata = this._docManager.getDocumentMetadata(documentId);
return [
{
@@ -1120,33 +387,12 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
case 'list': {
- // List all available documents in simple format
- const docs = Array.from(this.documentsById.entries()).map(([id, doc]) => ({
- id,
- title: doc.title || 'Untitled Document',
- type: doc.type || 'Unknown Type',
- }));
-
- if (docs.length === 0) {
- return [
- {
- type: 'text',
- text: 'No documents found in the current view.',
- },
- ];
- }
-
- return [
- {
- type: 'text',
- text: `Found ${docs.length} document(s) in the current view:\n${JSON.stringify(docs, null, 2)}`,
- },
- ];
+ this._docManager.listDocs();
}
case 'getFieldOptions': {
// Get all available field options with metadata
- const fieldOptions = this.getAllFieldMetadata();
+ const fieldOptions = this._docManager.getAllFieldMetadata();
return [
{
@@ -1171,61 +417,27 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
const title = String(args.title);
const data = String(args.data);
- // Validate doc_type
- if (!this.isValidDocType(docType)) {
+ const createdDoc = this._docManager.createDocInDash(docType, title, data);
+
+ if (!createdDoc) {
return [
{
type: 'text',
- text: `Error: Invalid doc_type. Valid options are: ${Object.keys(supportedDocTypes).join(',')}`,
+ text: 'Error: Failed to create document.',
},
];
}
- try {
- // Create simple document with just title and data
- const simpleDoc: parsedDoc = {
- doc_type: docType,
- title: title,
- data: data,
- x: 0,
- y: 0,
- _width: 300,
- _height: 300,
- _layout_fitWidth: false,
- _layout_autoHeight: true,
- };
-
- // Use the chatBox's createDocInDash method to create and link the document
- if (!this.chatBox || !this.chatBox.createDocInDash) {
- return [
- {
- type: 'text',
- text: 'Error: Could not access document creation functionality.',
- },
- ];
- }
-
- const createdDoc = this.chatBox.createDocInDash(simpleDoc);
-
- if (!createdDoc) {
- return [
- {
- type: 'text',
- text: 'Error: Failed to create document.',
- },
- ];
- }
+ // Update our local document maps with the new document
+ this._docManager.processDocument(createdDoc);
- // Update our local document maps with the new document
- this.processDocument(createdDoc);
+ // Get the created document's metadata
+ const createdMetadata = this._docManager.extractDocumentMetadata(this._docManager.createAgentDoc(createdDoc));
- // Get the created document's metadata
- const createdMetadata = this.extractDocumentMetadata(createdDoc);
-
- return [
- {
- type: 'text',
- text: `Document created successfully.
+ return [
+ {
+ type: 'text',
+ text: `Document created successfully.
Document ID: ${createdDoc.id}
Type: ${docType}
Title: "${title}"
@@ -1241,16 +453,8 @@ Next steps:
Full metadata for the created document:
${JSON.stringify(createdMetadata, null, 2)}`,
- },
- ];
- } catch (error) {
- return [
- {
- type: 'text',
- text: `Error creating document: ${error instanceof Error ? error.message : String(error)}`,
- },
- ];
- }
+ },
+ ];
}
default:
@@ -1370,38 +574,4 @@ ${JSON.stringify(createdMetadata, null, 2)}`,
return `Unknown action "${action}". Valid actions are "get", "edit", "list", "getFieldOptions", or "create".`;
}
}
-
- /**
- * Gets metadata for a specific document or all documents
- * @param documentId Optional ID of a specific document to get metadata for
- * @returns Document metadata or metadata for all documents
- */
- private getDocumentMetadata(documentId?: string): any {
- if (documentId) {
- const doc = this.documentsById.get(documentId);
- // Get metadata for a specific document
- return this.extractDocumentMetadata(doc);
- } else {
- // Get metadata for all documents
- const documentsMetadata: Record<string, any> = {};
- for (const doc of this.documentsById.values()) {
- documentsMetadata.add(this.extractDocumentMetadata(doc));
- }
-
- return {
- documentCount: this.documentsById.size,
- documents: documentsMetadata,
- fieldDefinitions: this.fieldMetadata,
- };
- }
- }
-
- /**
- * Helper method to validate a document type and ensure it's a valid supportedDocType
- * @param docType The document type to validate
- * @returns True if the document type is valid, false otherwise
- */
- private isValidDocType(docType: string): boolean {
- return Object.values(supportedDocTypes).includes(docType as supportedDocTypes);
- }
}