diff options
author | sharkiecodes <lanyi_stroud@brown.edu> | 2025-07-01 14:27:12 -0400 |
---|---|---|
committer | sharkiecodes <lanyi_stroud@brown.edu> | 2025-07-01 14:27:12 -0400 |
commit | d6672879b9e1dcb299eccd9818d4b214673c7a93 (patch) | |
tree | 6960befe51e0532046a24cb46c88ae13803f8040 | |
parent | b7d5510a600bbe7c6a4f32b528c93e4e6a79d7dd (diff) |
fixing sorting tagging tools
-rw-r--r-- | src/client/views/nodes/chatbot/tools/SortDocsTool.ts | 2 | ||||
-rw-r--r-- | src/client/views/nodes/chatbot/tools/TagDocsTool.ts | 158 |
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 |