aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsharkiecodes <lanyi_stroud@brown.edu>2025-07-10 10:38:23 -0400
committersharkiecodes <lanyi_stroud@brown.edu>2025-07-10 10:38:23 -0400
commit6ab42243141c2b6474cb4e85421d60f1827e403b (patch)
tree02cd9c32d4701e04293dc359934269ba4a6952f5 /src
parentb2b49aa38d6998c7fb8a44818fb6c6eb486d6d2e (diff)
fixed filterdoctool
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/chatbot/tools/FilterDocTool.ts161
1 files changed, 77 insertions, 84 deletions
diff --git a/src/client/views/nodes/chatbot/tools/FilterDocTool.ts b/src/client/views/nodes/chatbot/tools/FilterDocTool.ts
index 6be42d83b..d6d01ee82 100644
--- a/src/client/views/nodes/chatbot/tools/FilterDocTool.ts
+++ b/src/client/views/nodes/chatbot/tools/FilterDocTool.ts
@@ -7,13 +7,12 @@ import {
gptAPICall,
GPTCallType,
DescriptionSeperator,
- DataSeperator
+ DataSeperator,
} from '../../../../apis/gpt/GPT';
import { v4 as uuidv4 } from 'uuid';
import { TagItem } from '../../../TagsView';
import { DocumentView } from '../../DocumentView';
import { Doc } from '../../../../../fields/Doc';
-import { Filter } from '../../../../util/reportManager/ReportManagerComponents';
const parameterRules = [
{
@@ -28,108 +27,136 @@ const toolInfo: ToolInfo<typeof parameterRules> = {
name: 'filterDocs',
description: 'Filters documents based on user-specified natural-language criteria.',
parameterRules,
- citationRules:
- 'No citation needed for filtering operations.',
+ citationRules: 'No citation needed for filtering operations.',
};
export class FilterDocsTool extends BaseTool<typeof parameterRules> {
private _docManager: AgentDocumentManager;
static ChatTag = '#chat'; // tag used by GPT popup to filter docs
- // the parent Document (collection) that will be filtered
private _collectionView: DocumentView;
- constructor(
- docManager: AgentDocumentManager,
- collectionView: DocumentView
- ) {
+ constructor(docManager: AgentDocumentManager, collectionView: DocumentView) {
super(toolInfo);
this._docManager = docManager;
this._docManager.initializeFindDocsFreeform();
this._collectionView = collectionView;
}
- async execute(
- args: ParametersType<typeof parameterRules>
- ): Promise<Observation[]> {
+ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
const chunkId = uuidv4();
+ console.log('[FilterDocsTool] execute() called with criteria:', args.filterCriteria);
+
+ // 0) Check parent view & doc
+ const parentView = this._collectionView;
+ console.log('[FilterDocsTool] parentView:', parentView);
+ const parentDoc = parentView?.Document;
+ if (!parentDoc) {
+ console.error('[FilterDocsTool] Missing parentView.Document!');
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">
+FilterDocsTool: no parent DocumentView / parentDoc provided.
+</chunk>`,
+ },
+ ];
+ }
+ console.log('[FilterDocsTool] parentDoc:', parentDoc.id || parentDoc);
try {
// 1) Build description→ID map & prompt blocks
+ console.log('[FilterDocsTool] docIds:', this._docManager.docIds);
const textToId = new Map<string, string>();
const blocks: string[] = [];
for (const id of this._docManager.docIds) {
- // get a reliable human-readable description
- const desc = (
- await this._docManager.getDocDescription(id)
- )
- .replace(/\n/g, ' ')
- .trim();
-
- if (!desc) continue;
+ const descRaw = await this._docManager.getDocDescription(id);
+ const desc = descRaw.replace(/\n/g, ' ').trim();
+ console.log(`[FilterDocsTool] got desc for ${id}:`, desc);
+
+ if (!desc) {
+ console.warn(`[FilterDocsTool] skipping empty desc for ${id}`);
+ continue;
+ }
textToId.set(desc, id);
- blocks.push(`${DescriptionSeperator}${desc}${DescriptionSeperator}`);
+
+ const block = `${DescriptionSeperator}${desc}${DescriptionSeperator}`;
+ console.log('[FilterDocsTool] adding block:', block);
+ blocks.push(block);
}
const prompt = blocks.join('');
+ console.log('[FilterDocsTool] final prompt to GPT:', prompt);
// 2) Ask GPT for subset
- const raw = await gptAPICall(
- args.filterCriteria,
- GPTCallType.SUBSETDOCS,
- prompt
- );
- console.log('[FilterDocsTool] GPT response:', raw);
+ const raw = await gptAPICall(args.filterCriteria, GPTCallType.SUBSETDOCS, prompt);
+ console.log('[FilterDocsTool] GPT raw response:', raw);
// 3) Clear existing chat-filter tags/filters
const allDocs = this._docManager.docIds
.map((id) => this._docManager.getDocument(id))
.filter((d): d is Doc => !!d);
- allDocs.forEach((d) => {
- // remove any prior ChatTag
- TagItem.removeTagFromDoc(d, FilterDocsTool.ChatTag);
- });
- // also remove the docFilter setting on the parent
- Doc.setDocFilter(
- this._collectionView.Document,
- 'tags',
- FilterDocsTool.ChatTag,
- 'remove'
+ console.log('[FilterDocsTool] clearing existing tags from docs:', allDocs.map((d) => d.id || d));
+ allDocs.forEach((d) => TagItem.removeTagFromDoc(d, FilterDocsTool.ChatTag));
+
+ console.log(
+ `[FilterDocsTool] removing docFilter('${FilterDocsTool.ChatTag}') from parentDoc ${parentDoc.id}`
);
+ Doc.setDocFilter(parentDoc, 'tags', FilterDocsTool.ChatTag, 'remove');
// 4) Parse GPT’s output, re-apply tag + docFilter
+ const newlyTagged: string[] = [];
+
raw
.split(DescriptionSeperator)
.filter((blk) => blk.trim() !== '')
.map((blk) => blk.replace(/\n/g, ' ').trim())
- .forEach((blk) => {
- // split on '>>>>>>' aka DataSeperator
- const [descText, _extra] = blk.split(DataSeperator).map((s) => s.trim());
+ .forEach((blk, idx) => {
+ console.log(`[FilterDocsTool] parsing block[${idx}]:`, blk);
+ const [descText = '', _extra] = blk.split(DataSeperator).map((s) => s.trim());
+ console.log(` → descText = "${descText}", ignoring extra="${_extra}"`);
+
+ if (!descText) {
+ console.warn('[FilterDocsTool] skipping block with empty descText:', blk);
+ return;
+ }
+
const docId = textToId.get(descText);
if (!docId) {
- console.warn('[FilterDocsTool] no match for', descText);
+ console.warn('[FilterDocsTool] no match in textToId for descText:', descText);
return;
}
+
const doc = this._docManager.getDocument(docId);
- if (!doc) return;
+ if (!doc) {
+ console.warn('[FilterDocsTool] no Doc instance for ID:', docId);
+ return;
+ }
- // add the special '#chat' tag:
+ // apply the special '#chat' tag
+ console.log(`[FilterDocsTool] adding tag "${FilterDocsTool.ChatTag}" to doc ${docId}`);
TagItem.addTagToDoc(doc, FilterDocsTool.ChatTag);
+ newlyTagged.push(docId);
});
- // Finally, set the parent’s filter to **check** on that tag
- Doc.setDocFilter(
- this._collectionView.Document,
- 'tags',
- FilterDocsTool.ChatTag,
- 'check'
+ console.log('[FilterDocsTool] newly tagged IDs:', newlyTagged);
+
+ // 5) Finally, set the parent’s filter to **check** on that tag
+ console.log(
+ `[FilterDocsTool] setting docFilter('${FilterDocsTool.ChatTag}', 'check') on parentDoc ${parentDoc.id}`
);
+ Doc.setDocFilter(parentDoc, 'tags', FilterDocsTool.ChatTag, 'check');
+
+ // build summary
+ const summary = newlyTagged.length
+ ? newlyTagged.join(', ')
+ : '(none)';
return [
{
type: 'text',
text: `<chunk chunk_id="${chunkId}" chunk_type="filter_status">
-Filtered documents based on "${args.filterCriteria}". Only docs tagged "${FilterDocsTool.ChatTag}" will be shown.
+Filtered documents based on "${args.filterCriteria}". Docs tagged ${FilterDocsTool.ChatTag}: ${summary}
</chunk>`,
},
];
@@ -146,37 +173,3 @@ Filtering failed: ${err instanceof Error ? err.message : String(err)}
}
}
}
-
-/* processGptResponse = (docView: DocumentView, textToDocMap: Map<string, Doc>, gptOutput: string, questionType: GPTDocCommand) =>
- undoable(() => {
- switch (questionType) { // reset collection based on question typefc
- case GPTDocCommand.Sort:
- docView.Document[docView.ComponentView?.fieldKey + '_sort'] = docSortings.Chat;
- break;
- case GPTDocCommand.Filter:
- docView.ComponentView?.hasChildDocs?.().forEach(d => TagItem.removeTagFromDoc(d, GPTPopup.ChatTag));
- break;
- } // prettier-ignore
-
- gptOutput.split(DescriptionSeperator).filter(item => item.trim() !== '') // Split output into individual document contents
- .map(docContentRaw => docContentRaw.replace(/\n/g, ' ').trim())
- .map(docContentRaw => ({doc: textToDocMap.get(docContentRaw.split(DataSeperator)[0]), data: docContentRaw.split(DataSeperator)[1] })) // the find the corresponding Doc using textToDoc map
- .filter(({doc}) => doc).map(({doc, data}) => ({doc:doc!, data})) // filter out undefined values
- .forEach(({doc, data}, index) => {
- switch (questionType) {
- case GPTDocCommand.Sort:
- doc[ChatSortField] = index;
- break;
- case GPTDocCommand.AssignTags:
- data && TagItem.addTagToDoc(doc, data.startsWith('#') ? data : '#'+data[0].toLowerCase()+data.slice(1) );
- break;
- case GPTDocCommand.Filter:
- TagItem.addTagToDoc(doc, GPTPopup.ChatTag);
- Doc.setDocFilter(docView.Document, 'tags', GPTPopup.ChatTag, 'check');
- break;
- }
- }); // prettier-ignore
- }, '')();
-
- /**
- * When in quiz mode, rando*/ \ No newline at end of file