1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
|
import { Doc, FieldType } from '../../../../../fields/Doc';
import { DocData } from '../../../../../fields/DocSymbols';
import { Observation } from '../types/types';
import { ParametersType, ToolInfo, Parameter } from '../types/tool_types';
import { BaseTool } from './BaseTool';
import { DocumentOptions } from '../../../../documents/Documents';
import { CollectionFreeFormDocumentView } from '../../../nodes/CollectionFreeFormDocumentView';
import { v4 as uuidv4 } from 'uuid';
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> = [
{
name: 'action',
type: 'string',
required: true,
description: 'The action to perform: "get" to retrieve metadata, "edit" to modify metadata, "getFieldOptions" to retrieve all available field options, or "create" to create a new document',
},
{
name: 'documentId',
type: 'string',
required: false,
description: 'The ID of the document to get or edit metadata for. Required for "edit", optional for "get", ignored for "getFieldOptions", and "create"',
},
{
name: 'fieldEdits',
type: 'string',
required: false,
description:
'JSON array of field edits for editing fields. Each item should have fieldName and fieldValue. For single field edits, use an array with one item. Example: [{"fieldName":"layout_autoHeight","fieldValue":false},{"fieldName":"height","fieldValue":300}]',
},
{
name: 'title',
type: 'string',
required: false,
description: 'The title of the document to create. Required for "create" action',
},
{
name: 'data',
type: 'string',
required: false,
description: 'The data content for the document to create. Required for "create" action',
},
{
name: 'doc_type',
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;
// Detailed description with usage guidelines for the DocumentMetadataTool
const toolDescription = `Extracts and modifies metadata from documents in the same Freeform view as the ChatBox, and can create new documents.
This tool helps you work with document properties, understand available fields, edit document metadata, and create new documents.
The Dash document system organizes fields in two locations:
1. Layout documents: contain visual properties like position, dimensions, and appearance
2. Data documents: contain the actual content and document-specific data
This tool provides the following capabilities:
- Get metadata from all documents in the current Freeform view
- Get metadata from a specific document
- Edit metadata fields on documents (in either layout or data documents)
- Edit multiple fields at once (useful for updating dependent fields together)
- Retrieve all available field options with metadata (IMPORTANT: always call this before editing)
- Understand which fields are stored where (layout vs data document)
- Get detailed information about all available document fields
- Support for all value types: strings, numbers, and booleans
- Create new documents with basic properties
DOCUMENT CREATION:
- Use action="create" to create new documents with a simplified approach
- Required parameters: title, data, and doc_type
- The tool will create the document with sensible defaults and link it to the current view
- After creation, you can use the edit action to update its properties
IMPORTANT: Before editing any document metadata, first call 'getFieldOptions' to understand:
- Which fields are available
- The data type of each field
- Special dependencies between fields (like layout_autoHeight and height)
- Proper naming conventions (with or without underscores)
IMPORTANT: Some fields have dependencies that must be handled for edits to work correctly:
- When editing "height", first set "layout_autoHeight" to false (as a boolean value, not a string)
- When editing "width", first set "layout_autoWidth" to false (as a boolean value, not a string)
- Check document metadata to identify other similar dependencies
- All edits are done using the fieldEdits parameter which accepts an array of fields to modify
Example: To change document height, disable auto-height and set height in a single operation:
{... 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:
To GET document metadata:
- Use action="get" with optional documentId to return metadata for one or all documents
- Returns field values, field definitions, and location information (layout vs data document)
To GET ALL FIELD OPTIONS (call this first):
- Use action="getFieldOptions" to retrieve metadata about all available document fields
- No additional parameters are required
- Returns structured metadata with field names, types, descriptions, and dependencies
- ALWAYS call this before attempting to edit document metadata
- Use this information to understand which fields need special handling
To CREATE a new document:
- Use action="create" with the following required parameters:
- 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: {...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:
- Use action="edit" with required parameters:
- documentId: The ID of the document to edit
- fieldEdits: JSON array of fields to edit, each with fieldName and fieldValue
- The tool will determine the correct document location automatically
- Field names can be provided with or without leading underscores (e.g., both "width" and "_width" work)
- Common fields like "width" and "height" are automatically mapped to "_width" and "_height"
- All value types are supported: strings, numbers, and booleans
- The tool will apply the edit to the correct document (layout or data) based on existing fields
SPECIAL FIELD HANDLING:
- Text fields: When editing the 'text' field, provide simple plain text
Example: {...inputs: { action: "edit", documentId: "doc123", fieldEdits: [{ 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
RECOMMENDED WORKFLOW:
0. Understand the currently available documents that were provided as <available_documents> in the prompt
1. Call action="getFieldOptions" to understand available fields
3. Get document metadata with action="get" to see current values
4. Edit fields with action="edit" using proper dependencies
OR
1. Create a new document with action="create"
2. Get its ID from the response
3. Edit the document's properties with action="edit"
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:
{...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)
- Other auto-sizing related properties
Editing fields follows these rules:
1. First checks if the field exists on the layout document using Doc.Get
2. If it exists on the layout document, it's updated there
3. If it has an underscore prefix (_), it's created/updated on the layout document
4. Otherwise, the field is created/updated on the data document
5. Fields with leading underscores are automatically handled correctly
Examples:
- To get field options: { action: "getFieldOptions" }
- To get all document metadata: { action: "get" }
- To get metadata for a specific document: { action: "get", documentId: "doc123" }
- To edit a single field: { action: "edit", documentId: "doc123", fieldEdits: [{ fieldName: "backgroundColor", fieldValue: "#ff0000" }] }
- To edit a width property: { action: "edit", documentId: "doc123", fieldEdits: [{ fieldName: "width", fieldValue: 300 }] }
- To edit text content: { action: "edit", documentId: "doc123", fieldEdits: [{ fieldName: "text", fieldValue: "Simple plain text goes here" }] }
- To disable auto-height: { action: "edit", documentId: "doc123", fieldEdits: [{ fieldName: "layout_autoHeight", fieldValue: false }] }
- To create a text document: { action: "create", title: "My Notes", data: "This is my note content", doc_type: "text" }
- To create a web document: { action: "create", title: "Google", data: "https://www.google.com", doc_type: "web" }
- To edit height with its dependent field together:
{ 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.
- IMPORTANT: WHEN CITING A DOCUMENT, MAKE THE CHUNK ID THE DOCUMENT ID. WHENEVER YOU CITE A DOCUMENT, ALWAYS MAKE THE CITATION TYPE "text", THE "direct_text" FIELD BLANK, AND THE "chunk_id" FIELD THE DOCUMENT ID.`;
const documentMetadataToolInfo: ToolInfo<DocumentMetadataToolParamsType> = {
name: 'documentMetadata',
description: toolDescription,
parameterRules: parameterDefinitions,
citationRules: citationRules,
};
/**
* A tool for extracting and modifying metadata from documents in a Freeform view.
* This tool collects metadata from both layout and data documents in a Freeform view
* and allows for editing document fields in the correct location.
*/
export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsType> {
private _docManager: AgentDocumentManager;
constructor(docManager: AgentDocumentManager) {
super(documentMetadataToolInfo);
this._docManager = docManager;
this._docManager.initializeFindDocsFreeform();
}
/**
* Executes the document metadata tool
* @param args The arguments for the tool
* @returns An observation with the results of the tool execution
*/
async execute(args: ParametersType<DocumentMetadataToolParamsType>): Promise<Observation[]> {
console.log('DocumentMetadataTool: Executing with args:', args);
// Find all documents in the Freeform view
this._docManager.initializeFindDocsFreeform();
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))}`,
},
];
}
// Ensure the action is valid and convert to string
const action = String(args.action);
if (!['get', 'edit', 'getFieldOptions', 'create'].includes(action)) {
return [
{
type: 'text',
text: 'Error: Invalid action. Valid actions are "get", "edit", "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._docManager.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)}`,
},
];
}
case 'edit': {
// Edit a specific field on a document
if (!documentId) {
return [
{
type: 'text',
text: 'Error: Document ID is required for edit actions.',
},
];
}
// Ensure document exists
if (!this._docManager.has(documentId)) {
return [
{
type: 'text',
text: `Error: Document with ID ${documentId} not found.`,
},
];
}
// Check for fieldEdits parameter
if (!args.fieldEdits) {
return [
{
type: 'text',
text: 'Error: fieldEdits is required for edit actions. Please provide a JSON array of field edits.',
},
];
}
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.',
},
];
}
// Track results for all edits
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._docManager.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`;
}
});
} else {
responseText = `Errors occurred while editing fields on document ${documentId}:\n`;
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`;
}
} else {
responseText += `- Error editing '${result.originalFieldName}': ${result.message}\n`;
}
});
}
// Get the updated metadata to return
const updatedMetadata = this._docManager.getDocumentMetadata(documentId);
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)}`,
},
];
}
}
case 'getFieldOptions': {
// Get all available field options with metadata
const fieldOptions = this._docManager.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)}`,
},
];
}
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.',
},
];
}
const docType = String(args.doc_type);
const title = String(args.title);
const data = String(args.data);
const id = await this._docManager.createDocInDash(docType, data, { title: title });
if (!id) {
return [
{
type: 'text',
text: 'Error: Failed to create document.',
},
];
}
// Get the created document's metadata
const createdMetadata = this._docManager.extractDocumentMetadata(id);
return [
{
type: 'text',
text: `Document created successfully.
Document ID: ${id}
Type: ${docType}
Title: "${title}"
The document has been created with default dimensions and positioning.
You can now use the "edit" action to modify additional properties of this document.
Next steps:
1. Use the "getFieldOptions" action to understand available editable/addable fields/properties and their dependencies.
2. To modify this document, use: { action: "edit", documentId: "${id}", fieldEdits: [{"fieldName":"property","fieldValue":"value"}] }
3. To add styling, consider setting backgroundColor, fontColor, or other properties
4. For text documents, you can edit the content with: { action: "edit", documentId: "${id}", fieldEdits: [{"fieldName":"text","fieldValue":"New content"}] }
Full metadata for the created document:
${JSON.stringify(createdMetadata, null, 2)}`,
},
];
}
default:
return [
{
type: 'text',
text: 'Error: Unknown action. Valid actions are "get", "edit", "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)}`,
},
];
}
}
/**
* Validates the input parameters for the DocumentMetadataTool
* 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
*/
inputValidator(params: ParametersType<DocumentMetadataToolParamsType>): boolean {
// Default validation for required fields
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 fieldEdits is provided
if (params.action === 'edit') {
if (!params.documentId || !params.fieldEdits) {
return false;
}
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) {
console.log('An edit is missing fieldName');
return false;
}
if (edit.fieldValue === undefined) {
console.log('An edit is missing fieldValue');
return false;
}
}
// Everything looks good with fieldEdits
return true;
} catch (error) {
console.log('Error parsing fieldEdits:', error);
return false;
}
}
// 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;
}
return true;
}
/**
* Returns the parameter requirements for a specific action
* @param action The action to get requirements for
* @returns A string describing the required parameters
*/
private getParameterRequirementsByAction(action?: string): string {
if (!action) {
return 'Please specify an action: "get", "edit", "getFieldOptions", or "create".';
}
switch (action.toLowerCase()) {
case 'get':
return 'The "get" action accepts an optional documentId parameter.';
case 'edit':
return 'The "edit" action requires documentId and fieldEdits parameters. fieldEdits must be a JSON array of field edits.';
case 'getFieldOptions':
return 'The "getFieldOptions" action does not require any additional parameters. It returns metadata about all available document fields.';
case 'create':
return 'The "create" action requires title, data, and doc_type parameters.';
default:
return `Unknown action "${action}". Valid actions are "get", "edit", "getFieldOptions", or "create".`;
}
}
}
|