aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/chatbot/tools
diff options
context:
space:
mode:
authorA.J. Shulman <Shulman.aj@gmail.com>2025-04-10 12:36:07 -0400
committerA.J. Shulman <Shulman.aj@gmail.com>2025-04-10 12:36:07 -0400
commit6e8f05495dfcf7d64bdc424503f874fece85e291 (patch)
treee8f3ead08cbc1aa4728ed969c2ec3a35fadf201a /src/client/views/nodes/chatbot/tools
parentfa0b8fcd800e5a765a6db2681807986463544405 (diff)
trying again
Diffstat (limited to 'src/client/views/nodes/chatbot/tools')
-rw-r--r--src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts158
-rw-r--r--src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts561
2 files changed, 299 insertions, 420 deletions
diff --git a/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts b/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts
deleted file mode 100644
index 754d230c8..000000000
--- a/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { toLower } from 'lodash';
-import { Doc } from '../../../../../fields/Doc';
-import { Id } from '../../../../../fields/FieldSymbols';
-import { DocumentOptions } from '../../../../documents/Documents';
-import { parsedDoc } from '../chatboxcomponents/ChatBox';
-import { ParametersType, ToolInfo } from '../types/tool_types';
-import { Observation } from '../types/types';
-import { BaseTool } from './BaseTool';
-import { supportedDocTypes } from './CreateDocumentTool';
-
-const standardOptions = ['title', 'backgroundColor'];
-/**
- * Description of document options and data field for each type.
- */
-const documentTypesInfo: { [key in supportedDocTypes]: { options: string[]; dataDescription: string } } = {
- [supportedDocTypes.flashcard]: {
- options: [...standardOptions, 'fontColor', 'text_align'],
- dataDescription: 'an array of two strings. the first string contains a question, and the second string contains an answer',
- },
- [supportedDocTypes.text]: {
- options: [...standardOptions, 'fontColor', 'text_align'],
- dataDescription: 'The text content of the document.',
- },
- [supportedDocTypes.html]: {
- options: [],
- dataDescription: 'The HTML-formatted text content of the document.',
- },
- [supportedDocTypes.equation]: {
- options: [...standardOptions, 'fontColor'],
- dataDescription: 'The equation content as a string.',
- },
- [supportedDocTypes.functionplot]: {
- options: [...standardOptions, 'function_definition'],
- dataDescription: 'The function definition(s) for plotting. Provide as a string or array of function definitions.',
- },
- [supportedDocTypes.dataviz]: {
- options: [...standardOptions, 'chartType'],
- dataDescription: 'A string of comma-separated values representing the CSV data.',
- },
- [supportedDocTypes.notetaking]: {
- options: standardOptions,
- dataDescription: 'The initial content or structure for note-taking.',
- },
- [supportedDocTypes.rtf]: {
- options: standardOptions,
- dataDescription: 'The rich text content in RTF format.',
- },
- [supportedDocTypes.image]: {
- options: standardOptions,
- dataDescription: 'The image content as an image file URL.',
- },
- [supportedDocTypes.pdf]: {
- options: standardOptions,
- dataDescription: 'the pdf content as a PDF file url.',
- },
- [supportedDocTypes.audio]: {
- options: standardOptions,
- dataDescription: 'The audio content as a file url.',
- },
- [supportedDocTypes.video]: {
- options: standardOptions,
- dataDescription: 'The video content as a file url.',
- },
- [supportedDocTypes.message]: {
- options: standardOptions,
- dataDescription: 'The message content of the document.',
- },
- [supportedDocTypes.diagram]: {
- options: ['title', 'backgroundColor'],
- dataDescription: 'diagram content as a text string in Mermaid format.',
- },
- [supportedDocTypes.script]: {
- options: ['title', 'backgroundColor'],
- dataDescription: 'The compilable JavaScript code. Use this for creating scripts.',
- },
-};
-
-const createAnyDocumentToolParams = [
- {
- name: 'document_type',
- type: 'string',
- description: `The type of the document to create. Supported types are: ${Object.values(supportedDocTypes).join(', ')}`,
- required: true,
- },
- {
- name: 'data',
- type: 'string',
- description: 'The content or data of the document. The exact format depends on the document type.',
- required: true,
- },
- {
- name: 'options',
- type: 'string',
- required: false,
- description: `A JSON string representing the document options. Available options depend on the document type. For example:
- ${Object.entries(documentTypesInfo).map( ([doc_type, info]) => `
-- For '${doc_type}' documents, options include: ${info.options.join(', ')}`)
- .join('\n')}`, // prettier-ignore
- },
-] as const;
-
-type CreateAnyDocumentToolParamsType = typeof createAnyDocumentToolParams;
-
-const createAnyDocToolInfo: ToolInfo<CreateAnyDocumentToolParamsType> = {
- name: 'createAnyDocument',
- description:
- `Creates any type of document with the provided options and data.
- Supported document types are: ${Object.values(supportedDocTypes).join(', ')}.
- dataviz is a csv table tool, so for CSVs, use dataviz. Here are the options for each type:
- <supported_document_types>` +
- Object.entries(documentTypesInfo)
- .map(
- ([doc_type, info]) =>
- `<document_type name="${doc_type}">
- <data_description>${info.dataDescription}</data_description>
- <options>` +
- info.options.map(option => `<option>${option}</option>`).join('\n') +
- `</options>
- </document_type>`
- )
- .join('\n') +
- `</supported_document_types>`,
- parameterRules: createAnyDocumentToolParams,
- citationRules: 'No citation needed.',
-};
-
-export class CreateAnyDocumentTool extends BaseTool<CreateAnyDocumentToolParamsType> {
- private _addLinkedDoc: (doc: parsedDoc) => Doc | undefined;
-
- constructor(addLinkedDoc: (doc: parsedDoc) => Doc | undefined) {
- super(createAnyDocToolInfo);
- this._addLinkedDoc = addLinkedDoc;
- }
-
- async execute(args: ParametersType<CreateAnyDocumentToolParamsType>): Promise<Observation[]> {
- try {
- const documentType = toLower(args.document_type) as unknown as supportedDocTypes;
- const info = documentTypesInfo[documentType];
-
- if (info === undefined) {
- throw new Error(`Unsupported document type: ${documentType}. Supported types are: ${Object.values(supportedDocTypes).join(', ')}.`);
- }
-
- if (!args.data) {
- throw new Error(`Data is required for ${documentType} documents. ${info.dataDescription}`);
- }
-
- const options: DocumentOptions = !args.options ? {} : JSON.parse(args.options);
-
- // Call the function to add the linked document (add default title that can be overriden if set in options)
- const doc = this._addLinkedDoc({ doc_type: documentType, data: args.data, title: `New ${documentType.charAt(0).toUpperCase() + documentType.slice(1)} Document`, ...options });
-
- return [{ type: 'text', text: `Created ${documentType} document with ID ${doc?.[Id]}.` }];
- } catch (error) {
- return [{ type: 'text', text: 'Error creating document: ' + (error as Error).message }];
- }
- }
-}
diff --git a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
index a9fb45b5a..a3d86287d 100644
--- a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
+++ b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts
@@ -11,7 +11,6 @@ import { DocCast, StrCast } from '../../../../../fields/Types';
import { supportedDocTypes } from '../types/tool_types';
import { parsedDoc } from '../chatboxcomponents/ChatBox';
-
// Define the parameters for the DocumentMetadataTool
const parameterDefinitions: ReadonlyArray<Parameter> = [
{
@@ -61,7 +60,7 @@ const parameterDefinitions: ReadonlyArray<Parameter> = [
type: 'string',
required: false,
description: `The type of document to create. Required for "create" action. Options: ${Object.keys(supportedDocTypes).join(',')}`,
- }
+ },
] as const;
type DocumentMetadataToolParamsType = typeof parameterDefinitions;
@@ -105,14 +104,14 @@ IMPORTANT: Some fields have dependencies that must be handled for edits to work
- You can edit dependent fields in a single operation using the fieldEdits parameter
Example: To change document height, first disable auto-height:
-1. { action: "edit", documentId: "doc123", fieldName: "layout_autoHeight", fieldValue: false }
-2. { action: "edit", documentId: "doc123", fieldName: "height", fieldValue: 300 }
+1. {... inputs: { action: "edit", documentId: "doc123", fieldName: "layout_autoHeight", fieldValue: false }}
+2. {... inputs: { action: "edit", documentId: "doc123", fieldName: "height", fieldValue: 300 }}
OR using multi-field edit (recommended for dependent fields):
-{ action: "edit", documentId: "doc123", fieldEdits: [
+{... inputs: { action: "edit", documentId: "doc123", fieldEdits: [
{ fieldName: "layout_autoHeight", fieldValue: false },
{ fieldName: "height", fieldValue: 300 }
-]}`;
+]}}`;
// Extensive usage guidelines for the tool
const citationRules = `USAGE GUIDELINES:
@@ -132,7 +131,7 @@ To CREATE a new document:
- title: The title of the document to create
- data: The content data for the document (text content, URL, etc.)
- doc_type: The type of document to create (text, web, image, etc.)
-- Example: { action: "create", title: "My Notes", data: "This is the content", doc_type: "text" }
+- Example: {...inputs: { action: "create", title: "My Notes", data: "This is the content", doc_type: "text" }}
- After creation, you can edit the document with more specific properties
To EDIT document metadata:
@@ -147,7 +146,7 @@ To EDIT document metadata:
SPECIAL FIELD HANDLING:
- Text fields: When editing the 'text' field, provide simple plain text
- Example: { action: "edit", documentId: "doc123", fieldName: "text", fieldValue: "Hello world" }
+ Example: {...inputs: { action: "edit", documentId: "doc123", fieldName: "text", fieldValue: "Hello world" }}
The tool will automatically convert your text to the proper RichTextField format
- Width/Height: Set layout_autoHeight/layout_autoWidth to false before editing
@@ -165,10 +164,10 @@ HANDLING DEPENDENT FIELDS:
- When editing some fields, you may need to update related dependent fields
- For example, when changing "height", you should also set "layout_autoHeight" to false
- Use the fieldEdits parameter to update dependent fields in a single operation (recommended):
- { action: "edit", documentId: "doc123", fieldEdits: [
+ {...inputs: { action: "edit", documentId: "doc123", fieldEdits: [
{ fieldName: "layout_autoHeight", fieldValue: false },
{ fieldName: "height", fieldValue: 300 }
- ]}
+]}}
- Always check for dependent fields that might affect your edits, such as:
- height → layout_autoHeight (set to false to allow manual height)
- width → layout_autoWidth (set to false to allow manual width)
@@ -200,8 +199,8 @@ Examples:
{ action: "edit", documentId: "doc123", fieldEdits: [
{ fieldName: "layout_autoHeight", fieldValue: false },
{ fieldName: "height", fieldValue: 200 }
- ]}`;
-
+ ]}
+- IMPORTANT: MULTI STEP WORKFLOWS ARE NOT ONLY ALLOWED BUT ENCOURAGED. TAKE THINGS 1 STEP AT A TIME.`;
const documentMetadataToolInfo: ToolInfo<DocumentMetadataToolParamsType> = {
name: 'documentMetadata',
description: toolDescription,
@@ -227,7 +226,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
constructor(chatBox: any) {
super(documentMetadataToolInfo);
this.chatBox = chatBox;
-
+
// Store a direct reference to the ChatBox document
if (chatBox && chatBox.Document) {
this.chatBoxDocument = chatBox.Document;
@@ -246,7 +245,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
} else {
console.warn('DocumentMetadataTool initialized without valid ChatBox Document reference');
}
-
+
this.initializeFieldMetadata();
}
@@ -273,12 +272,12 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
// 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;
@@ -303,30 +302,30 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
// 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 {
@@ -356,7 +355,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
});
}
-
+
// Process the ChatBox document if available
if (this.chatBox.Document) {
this.processDocument(this.chatBox.Document);
@@ -366,11 +365,11 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
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;
@@ -396,17 +395,17 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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)
const layoutDoc = Doc.Layout(doc);
if (layoutDoc) {
this.layoutDocsById.set(docId, layoutDoc);
}
-
+
// Get data doc
const dataDoc = doc[DocData];
if (dataDoc) {
@@ -422,15 +421,15 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
*/
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;
@@ -448,14 +447,14 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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;
}
@@ -472,7 +471,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
const layoutDoc = this.layoutDocsById.get(docId);
const dataDoc = this.dataDocsById.get(docId);
-
+
const metadata: Record<string, any> = {
id: docId,
title: doc.title || '',
@@ -488,7 +487,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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) {
@@ -499,7 +498,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
metadata.fieldLocationMap[strippedName] = 'layout';
}
}
-
+
// Check if field exists on data document
let dataValue = undefined;
if (dataDoc) {
@@ -512,12 +511,12 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
}
}
-
+
// 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) {
@@ -525,7 +524,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
metadata.fieldLocationMap[nonUnderscoreFieldName] = 'layout';
}
}
-
+
if (dataDoc) {
const nonUnderscoreDataValue = dataDoc[nonUnderscoreFieldName];
if (nonUnderscoreDataValue !== undefined) {
@@ -544,7 +543,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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';
@@ -560,18 +559,22 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
* @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;
+ 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') {
@@ -579,36 +582,36 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
} 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;
@@ -618,13 +621,13 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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;
@@ -632,26 +635,26 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
} 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,
+
+ return {
+ success: true,
message: `Successfully updated field '${normalizedFieldName}' on ${targetLocation} document (ID: ${docId})`,
fieldName: normalizedFieldName,
originalFieldName: fieldName,
- newValue: convertedValue
+ 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)}`
+ return {
+ success: false,
+ message: `Error updating field: ${error instanceof Error ? error.message : String(error)}`,
};
}
}
@@ -667,7 +670,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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') {
@@ -677,12 +680,12 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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 {
@@ -710,28 +713,28 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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';
@@ -761,7 +764,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
return fieldValue;
}
}
-
+
// Default to string
return fieldValue;
}
@@ -775,7 +778,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
if (value === undefined || value === null) {
return null;
}
-
+
// Handle Doc objects
if (value instanceof Doc) {
return {
@@ -785,7 +788,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
docType: value.type || '',
};
}
-
+
// Handle RichTextField (try to extract plain text)
if (typeof value === 'string' && value.includes('"type":"doc"') && value.includes('"content":')) {
try {
@@ -802,9 +805,9 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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 {
@@ -819,14 +822,14 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
// 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);
@@ -834,7 +837,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
return '[Complex Object]';
}
}
-
+
// Return primitive values as is
return value;
}
@@ -856,7 +859,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
list: [],
date: [],
enumeration: [],
- other: []
+ other: [],
},
fieldNameMappings: {},
commonFields: {
@@ -865,19 +868,19 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
size: [],
content: [],
behavior: [],
- layout: []
- }
+ 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,
@@ -886,10 +889,10 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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') {
@@ -909,7 +912,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
} 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);
@@ -925,23 +928,23 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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'
+ 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'
- }
+ usage: 'To manually set width, first set layout_autoWidth to false',
+ },
};
-
+
// Add special section for text field format
result.specialFields = {
text: {
@@ -949,10 +952,10 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
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'
- }
+ example: 'For setting: "Hello world" (plain text); For getting: Will be converted to plaintext for display',
+ },
};
-
+
return result;
}
@@ -963,121 +966,129 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
*/
async execute(args: ParametersType<DocumentMetadataToolParamsType>): Promise<Observation[]> {
console.log('DocumentMetadataTool: Executing with args:', args);
-
+
// Find all documents in the Freeform view
this.findDocumentsInFreeformView();
-
+
try {
// Validate required input parameters based on action
if (!this.inputValidator(args)) {
- return [{
- type: 'text',
- text: `Error: Invalid or missing parameters for action "${args.action}". ${this.getParameterRequirementsByAction(String(args.action))}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error: Invalid or missing parameters for action "${args.action}". ${this.getParameterRequirementsByAction(String(args.action))}`,
+ },
+ ];
}
-
+
// Ensure the action is valid and convert to string
const action = String(args.action);
if (!['get', 'edit', 'list', 'getFieldOptions', 'create'].includes(action)) {
- return [{
- type: 'text',
- text: 'Error: Invalid action. Valid actions are "get", "edit", "list", "getFieldOptions", or "create".'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Invalid action. Valid actions are "get", "edit", "list", "getFieldOptions", or "create".',
+ },
+ ];
}
// Safely convert documentId to string or undefined
const documentId = args.documentId ? String(args.documentId) : undefined;
-
+
// Perform the specified action
switch (action) {
case 'get': {
// Get metadata for a specific document or all documents
const result = this.getDocumentMetadata(documentId);
console.log('DocumentMetadataTool: Get metadata result:', result);
- return [{
- type: 'text',
- text: `Document metadata ${documentId ? 'for document ' + documentId : ''} retrieved successfully:\n${JSON.stringify(result, null, 2)}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Document metadata ${documentId ? 'for document ' + documentId : ''} retrieved successfully:\n${JSON.stringify(result, null, 2)}`,
+ },
+ ];
}
-
+
case 'edit': {
// Edit a specific field on a document
if (!documentId) {
- return [{
- type: 'text',
- text: 'Error: Document ID is required for edit actions.'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Document ID is required for edit actions.',
+ },
+ ];
}
-
+
// Ensure document exists
if (!this.documentsById.has(documentId)) {
- return [{
- type: 'text',
- text: `Error: Document with ID ${documentId} not found.`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error: Document with ID ${documentId} not found.`,
+ },
+ ];
}
-
+
// Check if we're doing a multi-field edit or a single field edit
if (args.fieldEdits) {
try {
// Parse fieldEdits array
const edits = JSON.parse(String(args.fieldEdits));
if (!Array.isArray(edits) || edits.length === 0) {
- return [{
- type: 'text',
- text: 'Error: fieldEdits must be a non-empty array of field edits.'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: fieldEdits must be a non-empty array of field edits.',
+ },
+ ];
}
-
+
// Track results for all edits
- const results: {
- success: boolean;
- message: string;
- fieldName?: string;
- originalFieldName?: string;
+ const results: {
+ success: boolean;
+ message: string;
+ fieldName?: string;
+ originalFieldName?: string;
newValue?: any;
warning?: string;
}[] = [];
-
+
let allSuccessful = true;
-
+
// Process each edit
for (const edit of edits) {
// Get fieldValue in its original form
let fieldValue = edit.fieldValue;
-
+
// Only convert to string if it's neither boolean nor number
if (typeof fieldValue !== 'boolean' && typeof fieldValue !== 'number') {
fieldValue = String(fieldValue);
}
-
+
const fieldName = String(edit.fieldName);
-
+
// Edit the field
- const result = this.editDocumentField(
- documentId,
- fieldName,
- fieldValue
- );
-
+ const result = this.editDocumentField(documentId, fieldName, fieldValue);
+
console.log(`DocumentMetadataTool: Edit field result for ${fieldName}:`, result);
-
+
// Add to results
results.push(result);
-
+
// Update success status
if (!result.success) {
allSuccessful = false;
}
}
-
+
// Format response based on results
let responseText = '';
if (allSuccessful) {
responseText = `Successfully edited ${results.length} fields on document ${documentId}:\n`;
results.forEach(result => {
responseText += `- Field '${result.originalFieldName}': updated to ${JSON.stringify(result.newValue)}\n`;
-
+
// Add any warnings
if (result.warning) {
responseText += ` Warning: ${result.warning}\n`;
@@ -1088,7 +1099,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
results.forEach(result => {
if (result.success) {
responseText += `- Field '${result.originalFieldName}': updated to ${JSON.stringify(result.newValue)}\n`;
-
+
// Add any warnings
if (result.warning) {
responseText += ` Warning: ${result.warning}\n`;
@@ -1098,120 +1109,134 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
}
});
}
-
+
// Get the updated metadata to return
const updatedMetadata = this.getDocumentMetadata(documentId);
-
- return [{
- type: 'text',
- text: `${responseText}\nUpdated metadata:\n${JSON.stringify(updatedMetadata, null, 2)}`
- }];
+
+ return [
+ {
+ type: 'text',
+ text: `${responseText}\nUpdated metadata:\n${JSON.stringify(updatedMetadata, null, 2)}`,
+ },
+ ];
} catch (error) {
- return [{
- type: 'text',
- text: `Error processing fieldEdits: ${error instanceof Error ? error.message : String(error)}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error processing fieldEdits: ${error instanceof Error ? error.message : String(error)}`,
+ },
+ ];
}
} else {
// Single field edit (original behavior)
if (!args.fieldName) {
- return [{
- type: 'text',
- text: 'Error: Field name and field value are required for edit actions.'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Field name and field value are required for edit actions.',
+ },
+ ];
}
-
+
// Get fieldValue in its original form - we'll handle conversion in editDocumentField
let fieldValue = args.fieldValue;
-
+
// Only convert to string if it's neither boolean nor number
if (typeof fieldValue !== 'boolean' && typeof fieldValue !== 'number') {
fieldValue = String(fieldValue);
}
-
+
const fieldName = String(args.fieldName);
-
+
// Edit the field
- const result = this.editDocumentField(
- documentId,
- fieldName,
- fieldValue
- );
-
+ const result = this.editDocumentField(documentId, fieldName, fieldValue);
+
console.log('DocumentMetadataTool: Edit field result:', result);
-
+
if (!result.success) {
return [{ type: 'text', text: result.message }];
}
-
+
// Include warning if present
let responseText = result.message;
if (result.warning) {
responseText += `\n\n${result.warning}`;
}
-
+
// Get the updated metadata to return
const updatedMetadata = this.getDocumentMetadata(documentId);
-
- return [{
- type: 'text',
- text: `${responseText}\nUpdated metadata:\n${JSON.stringify(updatedMetadata, null, 2)}`
- }];
+
+ return [
+ {
+ type: 'text',
+ text: `${responseText}\nUpdated metadata:\n${JSON.stringify(updatedMetadata, null, 2)}`,
+ },
+ ];
}
}
-
+
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'
+ type: doc.type || 'Unknown Type',
}));
-
+
if (docs.length === 0) {
- return [{
- type: 'text',
- text: 'No documents found in the current view.'
- }];
+ 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)}`
- }];
+
+ return [
+ {
+ type: 'text',
+ text: `Found ${docs.length} document(s) in the current view:\n${JSON.stringify(docs, null, 2)}`,
+ },
+ ];
}
-
+
case 'getFieldOptions': {
// Get all available field options with metadata
const fieldOptions = this.getAllFieldMetadata();
-
- return [{
- type: 'text',
- text: `Document field options retrieved successfully.\nThis information should be consulted before editing document fields to understand available options and dependencies:\n${JSON.stringify(fieldOptions, null, 2)}`
- }];
+
+ return [
+ {
+ type: 'text',
+ text: `Document field options retrieved successfully.\nThis information should be consulted before editing document fields to understand available options and dependencies:\n${JSON.stringify(fieldOptions, null, 2)}`,
+ },
+ ];
}
-
+
case 'create': {
// Create a new document
if (!args.title || !args.data || !args.doc_type) {
- return [{
- type: 'text',
- text: 'Error: Title, data, and doc_type are required for create action.'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Title, data, and doc_type are required for create action.',
+ },
+ ];
}
-
+
const docType = String(args.doc_type);
const title = String(args.title);
const data = String(args.data);
-
+
// Validate doc_type
if (!this.isValidDocType(docType)) {
- return [{
- type: 'text',
- text: `Error: Invalid doc_type. Valid options are: ${Object.keys(supportedDocTypes).join(',')}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error: Invalid doc_type. Valid options are: ${Object.keys(supportedDocTypes).join(',')}`,
+ },
+ ];
}
-
+
try {
// Create simple document with just title and data
const simpleDoc: parsedDoc = {
@@ -1223,35 +1248,40 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp
_width: 300,
_height: 300,
_layout_fitWidth: false,
- _layout_autoHeight: true
+ _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.'
- }];
+ 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.'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Failed to create document.',
+ },
+ ];
}
-
+
// Update our local document maps with the new document
this.processDocument(createdDoc);
-
+
// Get the created document's metadata
const createdMetadata = this.getDocumentMetadata(createdDoc.id);
-
- return [{
- type: 'text',
- text: `Document created successfully.
+
+ return [
+ {
+ type: 'text',
+ text: `Document created successfully.
Document ID: ${createdDoc.id}
Type: ${docType}
Title: "${title}"
@@ -1266,36 +1296,43 @@ Next steps:
4. For text documents, you can edit the content with: { action: "edit", documentId: "${createdDoc.id}", fieldName: "text", fieldValue: "New content" }
Full metadata for the created document:
-${JSON.stringify(createdMetadata, null, 2)}`
- }];
+${JSON.stringify(createdMetadata, null, 2)}`,
+ },
+ ];
} catch (error) {
- return [{
- type: 'text',
- text: `Error creating document: ${error instanceof Error ? error.message : String(error)}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error creating document: ${error instanceof Error ? error.message : String(error)}`,
+ },
+ ];
}
}
-
+
default:
- return [{
- type: 'text',
- text: 'Error: Unknown action. Valid actions are "get", "edit", "list", "getFieldOptions", or "create".'
- }];
+ return [
+ {
+ type: 'text',
+ text: 'Error: Unknown action. Valid actions are "get", "edit", "list", "getFieldOptions", or "create".',
+ },
+ ];
}
} catch (error) {
console.error('DocumentMetadataTool execution error:', error);
- return [{
- type: 'text',
- text: `Error executing DocumentMetadataTool: ${error instanceof Error ? error.message : String(error)}`
- }];
+ return [
+ {
+ type: 'text',
+ text: `Error executing DocumentMetadataTool: ${error instanceof Error ? error.message : String(error)}`,
+ },
+ ];
}
}
/**
* Validates the input parameters for the DocumentMetadataTool
- * This custom validator allows numbers and booleans to be passed for fieldValue
+ * This custom validator allows numbers and booleans to be passed for fieldValue
* while maintaining compatibility with the standard validation
- *
+ *
* @param params The parameters to validate
* @returns True if the parameters are valid, false otherwise
*/
@@ -1304,12 +1341,12 @@ ${JSON.stringify(createdMetadata, null, 2)}`
if (params.action === undefined) {
return false;
}
-
+
// For create action, validate required parameters
if (params.action === 'create') {
return !!(params.title && params.data && params.doc_type);
}
-
+
// For edit action, validate either single field edit or multiple field edits
if (params.action === 'edit') {
// If fieldEdits is provided, it must be valid and we'll ignore fieldName/fieldValue
@@ -1317,13 +1354,13 @@ ${JSON.stringify(createdMetadata, null, 2)}`
try {
// Parse fieldEdits and validate its structure
const edits = JSON.parse(String(params.fieldEdits));
-
+
// Ensure it's an array
if (!Array.isArray(edits)) {
console.log('fieldEdits is not an array');
return false;
}
-
+
// Ensure each item has fieldName and fieldValue
for (const edit of edits) {
if (!edit.fieldName) {
@@ -1335,7 +1372,7 @@ ${JSON.stringify(createdMetadata, null, 2)}`
return false;
}
}
-
+
// Everything looks good with fieldEdits
return !!params.documentId; // Just ensure documentId is provided
} catch (error) {
@@ -1349,22 +1386,22 @@ ${JSON.stringify(createdMetadata, null, 2)}`
}
}
}
-
+
// For get action with documentId, documentId is required
if (params.action === 'get' && params.documentId === '') {
return false;
}
-
+
// getFieldOptions action doesn't require any additional parameters
if (params.action === 'getFieldOptions') {
return true;
}
-
+
// list action doesn't require any additional parameters
if (params.action === 'list') {
return true;
}
-
+
// Allow for numeric or boolean fieldValue even though the type is defined as string
if (params.fieldValue !== undefined) {
if (typeof params.fieldValue === 'number') {
@@ -1372,14 +1409,14 @@ ${JSON.stringify(createdMetadata, null, 2)}`
// We'll convert it later, so don't fail validation
return true;
}
-
+
if (typeof params.fieldValue === 'boolean') {
console.log('Boolean fieldValue detected, will be converted appropriately');
// We'll handle boolean conversion in the execute method
return true;
}
}
-
+
return true;
}
@@ -1424,11 +1461,11 @@ ${JSON.stringify(createdMetadata, null, 2)}`
for (const docId of this.documentsById.keys()) {
documentsMetadata[docId] = this.extractDocumentMetadata(docId);
}
-
+
return {
documentCount: this.documentsById.size,
documents: documentsMetadata,
- fieldDefinitions: this.fieldMetadata
+ fieldDefinitions: this.fieldMetadata,
};
}
}
@@ -1441,4 +1478,4 @@ ${JSON.stringify(createdMetadata, null, 2)}`
private isValidDocType(docType: string): boolean {
return Object.values(supportedDocTypes).includes(docType as supportedDocTypes);
}
-} \ No newline at end of file
+}