aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsharkiecodes <lanyi_stroud@brown.edu>2025-07-01 14:27:12 -0400
committersharkiecodes <lanyi_stroud@brown.edu>2025-07-01 14:27:12 -0400
commitd6672879b9e1dcb299eccd9818d4b214673c7a93 (patch)
tree6960befe51e0532046a24cb46c88ae13803f8040
parentb7d5510a600bbe7c6a4f32b528c93e4e6a79d7dd (diff)
fixing sorting tagging tools
-rw-r--r--src/client/views/nodes/chatbot/tools/SortDocsTool.ts2
-rw-r--r--src/client/views/nodes/chatbot/tools/TagDocsTool.ts158
2 files changed, 84 insertions, 76 deletions
diff --git a/src/client/views/nodes/chatbot/tools/SortDocsTool.ts b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
index e91a27b36..741a8f3ce 100644
--- a/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
@@ -46,6 +46,8 @@ export class SortDocsTool extends BaseTool<typeof parameterRules> {
const sortedIdsResponse = await gptAPICall(args.sortCriteria, GPTCallType.SORTDOCS, descriptions);
const sortedIds = sortedIdsResponse.trim().split('\n');
+ console.log(sortedIdsResponse);
+ console.log(sortedIds);
sortedIds.forEach((id, index) => {
this._docManager.editDocumentField(id, ChatSortField, index);
diff --git a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
index 2bd2059e6..6b4693279 100644
--- a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
@@ -2,98 +2,104 @@ import { BaseTool } from './BaseTool';
import { Observation } from '../types/types';
import { ParametersType, ToolInfo } from '../types/tool_types';
import { AgentDocumentManager } from '../utils/AgentDocumentManager';
-import { gptAPICall, GPTCallType } from '../../../../apis/gpt/GPT';
+import { gptAPICall, GPTCallType, DescriptionSeperator, DataSeperator } from '../../../../apis/gpt/GPT';
import { v4 as uuidv4 } from 'uuid';
-import { List } from '../../../../../fields/List';
-import { StrListCast } from '../../../../../fields/Doc';
+import { TagItem } from '../../../TagsView';
const parameterRules = [
- {
- name: 'taggingCriteria',
- type: 'string',
- description: 'Criteria provided by the user to generate and apply tags to the documents.',
- required: true,
- },
+ {
+ name: 'taggingCriteria',
+ type: 'string',
+ description: 'Natural-language criteria for tagging documents.',
+ required: true,
+ },
] as const;
const toolInfo: ToolInfo<typeof parameterRules> = {
- name: 'tagDocs',
- description:
- 'Generates and applies tags to documents within the Dash environment based on user-defined criteria, such as content, type, or custom metadata.',
- parameterRules,
- citationRules: 'No citation needed for tagging operations.',
+ name: 'tagDocs',
+ description: 'Automatically generate and apply tags to docs based on criteria.',
+ parameterRules,
+ citationRules: 'No citation needed for tagging operations.',
};
export class TagDocsTool extends BaseTool<typeof parameterRules> {
- private _docManager: AgentDocumentManager;
+ private _docManager: AgentDocumentManager;
- constructor(docManager: AgentDocumentManager) {
- super(toolInfo);
- this._docManager = docManager;
- this._docManager.initializeFindDocsFreeform();
- }
-
- async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
- const chunkId = uuidv4();
- try {
- const docs = this._docManager.docIds.map((id) => this._docManager.extractDocumentMetadata(id));
+ constructor(docManager: AgentDocumentManager) {
+ super(toolInfo);
+ this._docManager = docManager;
+ // make sure manager has scanned all docs
+ this._docManager.initializeFindDocsFreeform();
+ }
+async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+ try {
+ // Build a single string of all docs in the EXACT same format as GPTPopup does:
+ const descriptions = this._docManager.docIds
+ .map(id => this._docManager.extractDocumentMetadata(id))
+ .filter(m => m !== null)
+ .map(m => `${m!.id}${DataSeperator}${m!.title}`)
+ .map(str => `${DescriptionSeperator}${str}${DescriptionSeperator}`)
+ .join('');
- const descriptions = docs
- .filter((doc): doc is NonNullable<typeof doc> => doc !== null)
- .map(
- (doc) => `${doc.id}: ${doc.title} - ${doc.fields.layout.summary || ''}`
- )
- .join('\n');
- const taggingResultsRaw = await gptAPICall(args.taggingCriteria, GPTCallType.TAGDOCS, descriptions);
- console.log('<<TAG RAW>>', JSON.stringify(taggingResultsRaw));
- const taggingResults = taggingResultsRaw.trim().split('\n');
- const appliedTags: Record<string, string[]> = {};
+ // Call GPT
+ const raw = await gptAPICall(
+ args.taggingCriteria,
+ GPTCallType.TAGDOCS,
+ descriptions
+ );
- for (const line of taggingResults) {
- const [maybeId, maybeTags] = line.split(':');
- if (!maybeId || !maybeTags) continue;
+ // Prepare to collect what we actually applied
+ const appliedTags: Record<string, string[]> = {};
- const docId = maybeId.trim();
- const predictedTags = maybeTags
- .split(',')
- .map(t => t.trim())
- .filter(t => t.length > 0)
- .map(t => t.startsWith('#') ? t : `#${t}`);
+ // Parse and apply tags exactly like GPTPopup
+ raw
+ .split(DescriptionSeperator) // 1) break into blocks at "======"
+ .filter(block => block.trim() !== '') // 2) drop empty
+ .map(block => block.replace(/\n/g, ' ').trim()) // 3) flatten & trim
+ .map(block => {
+ const [idPart = '', tagsPart = ''] = block.split(DataSeperator);
+ return { id: idPart.trim(), tags: tagsPart.trim() };
+ })
+ .filter(({ id, tags }) => id && tags) // 4) valid pairs only
+ .forEach(({ id, tags }) => {
+ const doc = this._docManager.getDocument(id);
+ if (!doc) return;
- // 1) Retrieve the actual Doc from your manager
- const layoutDoc = this._docManager.getDocument(docId);
- if (!layoutDoc) {
- console.warn(`Doc ${docId} not found – skipping tags.`);
- continue;
- }
+ // Split tags, normalize, then apply
+ const normalized = tags
+ .split(',')
+ .map(t => t.trim())
+ .filter(t => t.length > 0)
+ .map(t => (t.startsWith('#') ? t : `#${t}`));
- // 2) Ensure the chat‐tag list exists (fieldKey might vary)
- // using a custom List field on the Doc called `$tags_chat`:
- let tagList = layoutDoc.$tags_chat as List<string> | undefined;
- if (!tagList) {
- tagList = new List<string>();
- layoutDoc.$tags_chat = tagList;
- }
+ normalized.forEach(tag => TagItem.addTagToDoc(doc, tag));
- // 3) Push each predicted tag (the List removes duplicates automatically)
- predictedTags.forEach(tag => tagList!.push(tag));
+ // Record for our summary
+ appliedTags[id] = normalized;
+ });
- // 4) Accumulate for reporting
- appliedTags[docId] = StrListCast(tagList)
- }
-
- const resultSummary = Object.entries(appliedTags)
+ // Build single observation with summary
+ const summary = Object.entries(appliedTags)
.map(([id, tags]) => `${id}: ${tags.join(', ')}`)
.join('; ');
- return [{
- type: 'text',
- text: `<chunk chunk_id="${chunkId}" chunk_type="tagging_status">Successfully tagged documents based on "${args.taggingCriteria}". Tags applied: ${resultSummary}</chunk>`,
- }];
-} catch (error) {
- return [{
- type: 'text',
- text: `<chunk chunk_id="${chunkId}" chunk_type="error">Tagging failed: ${error instanceof Error ? error.message : String(error)}</chunk>`,
- }];
-}
-}
-}
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="tagging_status">
+Successfully tagged documents based on "${args.taggingCriteria}". Tags applied: ${summary}
+</chunk>`,
+ },
+ ];
+ } catch (e) {
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">
+Tagging failed: ${e instanceof Error ? e.message : String(e)}
+</chunk>`,
+ },
+ ];
+ }
+ }
+} \ No newline at end of file