aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/chatbot/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/chatbot/tools')
-rw-r--r--src/client/views/nodes/chatbot/tools/SortDocsTool.ts121
-rw-r--r--src/client/views/nodes/chatbot/tools/TagDocsTool.ts54
-rw-r--r--src/client/views/nodes/chatbot/tools/TakeQuizTool.ts88
3 files changed, 208 insertions, 55 deletions
diff --git a/src/client/views/nodes/chatbot/tools/SortDocsTool.ts b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
index 741a8f3ce..45d7b4f15 100644
--- a/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/SortDocsTool.ts
@@ -2,66 +2,97 @@ 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 } from '../../../../apis/gpt/GPT';
import { ChatSortField } from '../../../collections/CollectionSubView';
import { v4 as uuidv4 } from 'uuid';
+import { DocumentView } from '../../DocumentView';
+import { docSortings } from '../../../collections/CollectionSubView';
+import { collect } from '@turf/turf';
const parameterRules = [
- {
- name: 'sortCriteria',
- type: 'string',
- description: 'Criteria provided by the user to sort the documents.',
- required: true,
- },
+ {
+ name: 'sortCriteria',
+ type: 'string',
+ description: 'Criteria provided by the user to sort the documents.',
+ required: true,
+ },
] as const;
const toolInfo: ToolInfo<typeof parameterRules> = {
- name: 'sortDocs',
- description:
- 'Sorts documents within the current Dash environment based on user-specified criteria. Provide clear sorting criteria, such as by date, title, relevance, or custom metadata fields.',
- parameterRules,
- citationRules: 'No citation needed for sorting operations.',
+ name: 'sortDocs',
+ description:
+ 'Sorts documents within the current Dash environment based on user-specified criteria.',
+ parameterRules,
+ citationRules: 'No citation needed for sorting operations.',
};
export class SortDocsTool extends BaseTool<typeof parameterRules> {
- private _docManager: AgentDocumentManager;
+ private _docManager: AgentDocumentManager;
+ private _collectionView: DocumentView;
- constructor(docManager: AgentDocumentManager) {
- super(toolInfo);
- this._docManager = docManager;
- this._docManager.initializeFindDocsFreeform();
- }
+ constructor(docManager: AgentDocumentManager, collectionView: DocumentView)
+ {
+ super(toolInfo);
+ // Grab the parent collection’s DocumentView (the ChatBox container)
+ // We assume the ChatBox itself is currently selected in its parent view.
+ this._collectionView = collectionView;
+ this._docManager = docManager;
+ this._docManager.initializeFindDocsFreeform();
+ }
+
+ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+
+ // 1) gather metadata & build map from text→id
+ const textToId = new Map<string, string>();
+
+ const chunks = (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('');
+ try {
+ // 2) call GPT to sort those chunks
+ const gptResponse = await gptAPICall(args.sortCriteria, GPTCallType.SORTDOCS, chunks);
+ console.log('GPT RESP:', gptResponse);
- async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
- const chunkId = uuidv4();
- try {
- const docs = this._docManager.docIds.map((id) => this._docManager.extractDocumentMetadata(id));
+ // 3) parse & map back to IDs
+ const sortedIds = gptResponse
+ .split(DescriptionSeperator)
+ .filter(s => s.trim() !== '')
+ .map(s => s.replace(/\n/g, ' ').trim())
+ .map(s => textToId.get(s)) // lookup in our map
+ .filter((id): id is string => !!id);
- const descriptions = docs
- .filter((doc): doc is NonNullable<typeof doc> => doc !== null)
- .map(
- (doc) => `${doc.id}: ${doc.title} - ${doc.fields.layout.summary || ''}`
- )
- .join('\n');
+ // 4) write back the ordering
+ sortedIds.forEach((docId, idx) => {
+ this._docManager.editDocumentField(docId, ChatSortField, idx);
+ });
- const sortedIdsResponse = await gptAPICall(args.sortCriteria, GPTCallType.SORTDOCS, descriptions);
- const sortedIds = sortedIdsResponse.trim().split('\n');
- console.log(sortedIdsResponse);
- console.log(sortedIds);
+ const fieldKey = this._collectionView.ComponentView!.fieldKey;
+ this._collectionView.Document[ `${fieldKey}_sort` ] = docSortings.Chat;
- sortedIds.forEach((id, index) => {
- this._docManager.editDocumentField(id, ChatSortField, index);
- });
- return [{
- type: 'text',
- text: `<chunk chunk_id="${chunkId}" chunk_type="sort_status">Successfully sorted ${sortedIds.length} documents based on "${args.sortCriteria}".</chunk>`,
- }];
- } catch (error) {
- return [{
- type: 'text',
- text: `<chunk chunk_id="${chunkId}" chunk_type="error">Sorting failed: ${error instanceof Error ? error.message : String(error)}</chunk>`,
- }];
- }
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="sort_status">
+Successfully sorted ${sortedIds.length} documents by "${args.sortCriteria}".
+</chunk>`,
+ },
+ ];
+ } catch (err) {
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">
+Sorting failed: ${err instanceof Error ? err.message : err}
+</chunk>`,
+ },
+ ];
}
+ }
}
diff --git a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
index 6b4693279..75f476348 100644
--- a/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/TagDocsTool.ts
@@ -34,25 +34,30 @@ 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('');
-
+
+ // 1) gather metadata & build map from text→id
+ 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 raw = await gptAPICall(
args.taggingCriteria,
GPTCallType.TAGDOCS,
descriptions
);
-
+ console.log('TAG RESP:', raw);
// Prepare to collect what we actually applied
const appliedTags: Record<string, string[]> = {};
// Parse and apply tags exactly like GPTPopup
- raw
+ /*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
@@ -76,7 +81,36 @@ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[
// 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
const summary = Object.entries(appliedTags)
diff --git a/src/client/views/nodes/chatbot/tools/TakeQuizTool.ts b/src/client/views/nodes/chatbot/tools/TakeQuizTool.ts
new file mode 100644
index 000000000..f025e95cd
--- /dev/null
+++ b/src/client/views/nodes/chatbot/tools/TakeQuizTool.ts
@@ -0,0 +1,88 @@
+import { BaseTool } from './BaseTool';
+import { Observation } from '../types/types';
+import { ParametersType, ToolInfo } from '../types/tool_types';
+import { AgentDocumentManager } from '../utils/AgentDocumentManager';
+import { GPTCallType, gptAPICall } from '../../../../apis/gpt/GPT';
+import { v4 as uuidv4 } from 'uuid';
+
+const parameterRules = [
+ {
+ name: 'userAnswer',
+ type: 'string',
+ description: 'User-provided answer to the quiz question.',
+ required: true,
+ },
+] as const;
+
+const toolInfo: ToolInfo<typeof parameterRules> = {
+ name: 'takeQuiz',
+ description:
+ 'Evaluates a user\'s answer for a randomly selected document using GPT, mirroring GPTPopup\'s quiz functionality.',
+ parameterRules,
+ citationRules: 'No citation needed for quiz operations.',
+};
+
+export class TakeQuizTool extends BaseTool<typeof parameterRules> {
+ private _docManager: AgentDocumentManager;
+
+ constructor(docManager: AgentDocumentManager) {
+ super(toolInfo);
+ this._docManager = docManager;
+ this._docManager.initializeFindDocsFreeform();
+ }
+
+ private async generateRubric(docId: string, description: string): Promise<string> {
+ const docMeta = this._docManager.extractDocumentMetadata(docId);
+ if (docMeta && docMeta.fields.layout.gptRubric) {
+ return docMeta.fields.layout.gptRubric;
+ } else {
+ const rubric = await gptAPICall(description, GPTCallType.MAKERUBRIC);
+ if (rubric) {
+ await this._docManager.editDocumentField(docId, 'layout.gptRubric', rubric);
+ }
+ return rubric || '';
+ }
+ }
+
+ async execute(args: ParametersType<typeof parameterRules>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+
+ try {
+ const allDocIds = this._docManager.docIds;
+ const randomDocId = allDocIds[Math.floor(Math.random() * allDocIds.length)];
+ const docMeta = this._docManager.extractDocumentMetadata(randomDocId);
+
+ if (!docMeta) throw new Error('Randomly selected document metadata is undefined');
+
+ const description = docMeta.fields.layout.description.replace(/\n/g, ' ').trim();
+ const rubric = await this.generateRubric(randomDocId, description);
+
+ const prompt = `
+ Question: ${description};
+ UserAnswer: ${args.userAnswer};
+ Rubric: ${rubric}
+ `;
+
+ const evaluation = await gptAPICall(prompt, GPTCallType.QUIZDOC);
+
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="quiz_result">
+Evaluation result: ${evaluation || 'GPT provided no answer'}.
+Document evaluated: "${docMeta.title}"
+</chunk>`,
+ },
+ ];
+ } catch (err) {
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">
+Quiz evaluation failed: ${err instanceof Error ? err.message : err}
+</chunk>`,
+ },
+ ];
+ }
+ }
+}