aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/prompts.ts1
-rw-r--r--src/client/views/nodes/chatbot/tools/TagDocsTool.ts137
2 files changed, 63 insertions, 75 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/prompts.ts b/src/client/views/nodes/chatbot/agentsystem/prompts.ts
index b7678bd08..ed97fd5c1 100644
--- a/src/client/views/nodes/chatbot/agentsystem/prompts.ts
+++ b/src/client/views/nodes/chatbot/agentsystem/prompts.ts
@@ -110,6 +110,7 @@ export function getReactPrompt(tools: BaseTool<ReadonlyArray<Parameter>>[], summ
<tools>
${toolDescriptions}
+ <note>The tagging tool takes priority over the metadata tool for queries relating to tagging.</note>
<note>If no external tool is required, use 'no_tool', but if there might be relevant external information, use the appropriate tool.</note>
</tools>
diff --git a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
index 75f476348..c88c32e50 100644
--- a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
@@ -5,11 +5,12 @@ import { AgentDocumentManager } from '../utils/AgentDocumentManager';
import { gptAPICall, GPTCallType, DescriptionSeperator, DataSeperator } from '../../../../apis/gpt/GPT';
import { v4 as uuidv4 } from 'uuid';
import { TagItem } from '../../../TagsView';
+
const parameterRules = [
{
name: 'taggingCriteria',
type: 'string',
- description: 'Natural-language criteria for tagging documents.',
+ description: 'Natural‐language criteria for tagging documents.',
required: true,
},
] as const;
@@ -27,92 +28,77 @@ export class TagDocsTool extends BaseTool<typeof parameterRules> {
constructor(docManager: AgentDocumentManager) {
super(toolInfo);
this._docManager = docManager;
- // make sure manager has scanned all docs
+ // ensure our manager has scanned all freeform docs
this._docManager.initializeFindDocsFreeform();
}
-async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+
+ 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:
-
- // 1) gather metadata & build map from text→id
+ // 1) Build description→ID map and the prompt string
const textToId = new Map<string, string>();
- //make this a general UTIL
- const descriptions = (await Promise.all(
- this._docManager.docIds.map(async id => {
- const text = await this._docManager.getDocDescription(id);
- textToId.set(text,id);
- return DescriptionSeperator + text + DescriptionSeperator;
- })
- ))
- .join('');
- // Call GPT
+ const descriptionsArray: string[] = [];
+
+ // We assume getDocDescription returns a Promise<string> with the text
+ for (const id of this._docManager.docIds) {
+ const desc = (await this._docManager.getDocDescription(id)).replace(/\n/g, ' ').trim();
+ if (!desc) continue;
+ textToId.set(desc, id);
+ // wrap in separators exactly as GPTPopup does
+ descriptionsArray.push(`${DescriptionSeperator}${desc}${DescriptionSeperator}`);
+ }
+
+ const promptDescriptions = descriptionsArray.join('');
+
+ // 2) Call GPT
const raw = await gptAPICall(
args.taggingCriteria,
GPTCallType.TAGDOCS,
- descriptions
+ promptDescriptions
);
- console.log('TAG RESP:', raw);
- // Prepare to collect what we actually applied
+ console.log('[TagDocsTool] GPT raw:', raw);
+
+ // 3) Parse GPT’s response, look up each description, and apply tags
const appliedTags: Record<string, string[]> = {};
- // 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;
-
- // Split tags, normalize, then apply
- const normalized = tags
+ raw
+ .split(DescriptionSeperator) // split into blocks
+ .filter(block => block.trim() !== '') // remove empties
+ .map(block => block.replace(/\n/g, ' ').trim()) // flatten whitespace
+ .forEach(block => {
+ // each block should look like: "<desc_text>>>>><tag1,tag2>"
+ const [descText, tagsText = ''] = block.split(DataSeperator).map(s => s.trim());
+ if (!descText || !tagsText) {
+ console.warn('[TagDocsTool] skipping invalid block:', block);
+ return;
+ }
+ const docId = textToId.get(descText);
+ if (!docId) {
+ console.warn('[TagDocsTool] no docId for description:', descText);
+ return;
+ }
+ const doc = this._docManager.getDocument(docId);
+ if (!doc) {
+ console.warn('[TagDocsTool] no Doc instance for ID:', docId);
+ return;
+ }
+
+ // split/normalize tags
+ const normalized = tagsText
.split(',')
- .map(t => t.trim())
- .filter(t => t.length > 0)
- .map(t => (t.startsWith('#') ? t : `#${t}`));
+ .map(tag => tag.trim())
+ .filter(tag => tag.length > 0)
+ .map(tag => (tag.startsWith('#') ? tag : `#${tag}`));
+ // apply tags
normalized.forEach(tag => TagItem.addTagToDoc(doc, tag));
- // Record for our summary
- appliedTags[id] = normalized;
- });*/
-
- raw
- .split(DescriptionSeperator) // 1) Split into “blocks”
- .filter(item => item.trim() !== '') // 2) Drop empty blocks
- .map(block => block.replace(/\n/g, ' ').trim()) // 3) Flatten & trim
- .map(block => {
- // 4) block looks like: "docId>>>>>>tag1, tag2"
- const [idPart, tagsPart] = block.split(DataSeperator);
- return { id: idPart.trim(), data: (tagsPart || '').trim() };
- })
- .filter(({ id, data }) => !!id && !!data) // 5) Keep only valid pairs
- .forEach(({ id, data }) => {
- // 6) Lookup doc by ID
- const doc = this._docManager.getDocument(id);
- if (!doc) return;
-
- // 7) Only in the AssignTags branch do we add tags
- // And GPTPopup normalizes by lowercasing first letter if no '#'
- const tagToApply = data.startsWith('#')
- ? data
- : '#' + data[0].toLowerCase() + data.slice(1);
-
- TagItem.addTagToDoc(doc, tagToApply);
-
- // 8) Record for summary
- if (!appliedTags[id]) appliedTags[id] = [];
- appliedTags[id].push(tagToApply);
- });
-
-
- // Build single observation with summary
+ // record for our summary
+ appliedTags[docId] = normalized;
+ });
+
+ // 4) Build a summary observation
const summary = Object.entries(appliedTags)
.map(([id, tags]) => `${id}: ${tags.join(', ')}`)
.join('; ');
@@ -125,15 +111,16 @@ Successfully tagged documents based on "${args.taggingCriteria}". Tags applied:
</chunk>`,
},
];
- } catch (e) {
+ } catch (err) {
+ console.error('[TagDocsTool] error:', err);
return [
{
type: 'text',
text: `<chunk chunk_id="${chunkId}" chunk_type="error">
-Tagging failed: ${e instanceof Error ? e.message : String(e)}
+Tagging failed: ${err instanceof Error ? err.message : String(err)}
</chunk>`,
},
];
}
}
-} \ No newline at end of file
+}