aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/Agent.ts5
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx37
-rw-r--r--src/client/views/nodes/chatbot/tools/TutorialTool.ts205
3 files changed, 219 insertions, 28 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
index 8516f054b..361c5eb2b 100644
--- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts
+++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
@@ -7,17 +7,14 @@ import { AnswerParser } from '../response_parsers/AnswerParser';
import { StreamedAnswerParser } from '../response_parsers/StreamedAnswerParser';
import { BaseTool } from '../tools/BaseTool';
import { CalculateTool } from '../tools/CalculateTool';
-//import { CreateAnyDocumentTool } from '../tools/CreateAnyDocTool';
import { DataAnalysisTool } from '../tools/DataAnalysisTool';
import { DocumentMetadataTool } from '../tools/DocumentMetadataTool';
-import { ImageCreationTool } from '../tools/ImageCreationTool';
import { NoTool } from '../tools/NoTool';
import { SearchTool } from '../tools/SearchTool';
import { Parameter, ParametersType, TypeMap } from '../types/tool_types';
import { AgentMessage, ASSISTANT_ROLE, AssistantMessage, Observation, PROCESSING_TYPE, ProcessingInfo, TEXT_TYPE } from '../types/types';
import { Vectorstore } from '../vectorstore/Vectorstore';
import { getReactPrompt } from './prompts';
-//import { DictionaryTool } from '../tools/DictionaryTool';
import { ChatCompletionMessageParam } from 'openai/resources';
import { Doc } from '../../../../../fields/Doc';
import { ChatBox, parsedDoc } from '../chatboxcomponents/ChatBox';
@@ -297,7 +294,7 @@ export class Agent {
ignoreAttributes: false,
attributeNamePrefix: '@_',
textNodeName: '_text',
- isArray: name => ['query', 'url'].indexOf(name) !== -1,
+ isArray: name => name === 'url',
processEntities: false, // Disable processing of entities
stopNodes: ['*.entity'], // Do not process any entities
});
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index df6c5627c..db01b7c88 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -374,7 +374,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
}
});
- this.scrollToBottom();
};
const onAnswerUpdate = (answerUpdate: string) => {
@@ -382,41 +381,29 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
if (this._current_message) {
this._current_message = {
...this._current_message,
- content: [{ text: answerUpdate, type: TEXT_TYPE.NORMAL, index: 0, citation_ids: [] }],
+ content: [{ index: 0, type: TEXT_TYPE.NORMAL, text: answerUpdate, citation_ids: null }],
};
}
});
};
- // Send the user's question to the assistant and get the final message
- const finalMessage = await this.agent.askAgent(trimmedText, onProcessingUpdate, onAnswerUpdate);
+ // Get the response from the agent
+ const response = await this.agent.askAgent(trimmedText, onProcessingUpdate, onAnswerUpdate);
- // Update the history with the final assistant message
+ // Push the final message to history
runInAction(() => {
- if (this._current_message) {
- this._history.push({ ...finalMessage });
- this._current_message = undefined;
- this.dataDoc.data = JSON.stringify(this._history);
- }
+ this._history.push(response);
+ this._isLoading = false;
+ this._current_message = undefined;
});
- } catch (err) {
- console.error('Error:', err);
- // Handle error in processing
- runInAction(() =>
- this._history.push({
- role: ASSISTANT_ROLE.ASSISTANT,
- content: [{ index: 0, type: TEXT_TYPE.ERROR, text: `Sorry, I encountered an error while processing your request: ${err} `, citation_ids: null }],
- processing_info: [],
- })
- );
- } finally {
+ } catch (error) {
+ console.error('Error in askGPT:', error);
runInAction(() => {
this._isLoading = false;
+ this._current_message = undefined;
});
- this.scrollToBottom();
}
}
- this.scrollToBottom();
};
/**
@@ -1066,7 +1053,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
{
index: 0,
type: TEXT_TYPE.NORMAL,
- text: `Hey, ${this.userName()}! Welcome to Your Friendly Assistant. Link a document or ask questions to get started.`,
+ text: this.dataDoc.is_dash_doc_assistant
+ ? 'Welcome to your help assistant for Dash. Ask any Dash-related questions to get started.'
+ : `Hey, ${this.userName()}! Welcome to Your Friendly Assistant. Link a document or ask questions to get started.`,
citation_ids: null,
},
],
diff --git a/src/client/views/nodes/chatbot/tools/TutorialTool.ts b/src/client/views/nodes/chatbot/tools/TutorialTool.ts
new file mode 100644
index 000000000..08e4e1409
--- /dev/null
+++ b/src/client/views/nodes/chatbot/tools/TutorialTool.ts
@@ -0,0 +1,205 @@
+import { BaseTool } from './BaseTool';
+import { Observation } from '../types/types';
+import { ParametersType, ToolInfo } from '../types/tool_types';
+import { schema } from '../../../../views/nodes/formattedText/schema_rts';
+import { v4 as uuidv4 } from 'uuid';
+import { gptTutorialAPICall } from '../../../../apis/gpt/TutorialGPT';
+import { parsedDoc } from '../chatboxcomponents/ChatBox';
+import { Id } from '../../../../../fields/FieldSymbols';
+import { Doc } from '../../../../../fields/Doc';
+import { RichTextField } from '../../../../../fields/RichTextField';
+import { DocumentViewInternal } from '../../DocumentView';
+import { Docs } from '../../../../documents/Documents';
+import { OpenWhere } from '../../OpenWhere';
+import { CollectionFreeFormView } from '../../../collections/collectionFreeForm';
+
+const generateTutorialNodeToolParams = [
+ {
+ name: 'query',
+ type: 'string',
+ description: 'The user query that asks how to use the environment',
+ required: true,
+ },
+] as const;
+
+const generateTutorialNodeToolInfo: ToolInfo<typeof generateTutorialNodeToolParams> = {
+ name: 'generateTutorialNode',
+ description: "Generates a tutorial text node based on the user's query about Dash functionality. Use this when the user asks for help or tutorials on how to use Dash features.",
+ parameterRules: generateTutorialNodeToolParams,
+ citationRules: "No citation needed for this tool's output.",
+};
+const applyFormatting = (markdownText: string): { doc: any; plainText: string } => {
+ const lines = markdownText.split('\n');
+ const nodes: any[] = [];
+ let plainText = '';
+ let i = 0;
+ let currentListItems: any[] = [];
+ let currentParagraph: any[] = [];
+ let currentOrderedListItems: any[] = [];
+ let inOrderedList = false;
+ let inBulletList = false;
+
+ const processBoldText = (text: string) => {
+ const boldRegex = /\*\*(.*?)\*\*/g;
+ const parts = [];
+ let lastIndex = 0;
+ let match;
+
+ while ((match = boldRegex.exec(text)) !== null) {
+ if (match.index > lastIndex) {
+ parts.push(schema.text(text.substring(lastIndex, match.index)));
+ }
+ parts.push(schema.text(match[1], [schema.marks.strong.create()]));
+ lastIndex = match.index + match[0].length;
+ }
+ if (lastIndex < text.length) {
+ parts.push(schema.text(text.substring(lastIndex)));
+ }
+ return parts.length > 0 ? parts : [schema.text(text)];
+ };
+
+ const flushListItems = () => {
+ if (currentListItems.length > 0) {
+ nodes.push(schema.nodes.ordered_list.create({ mapStyle: 'bullet' }, currentListItems));
+ nodes.push(schema.nodes.paragraph.create());
+ currentListItems = [];
+ inBulletList = false;
+ }
+ if (currentOrderedListItems.length > 0) {
+ nodes.push(schema.nodes.ordered_list.create({ mapStyle: 'number' }, currentOrderedListItems));
+ nodes.push(schema.nodes.paragraph.create());
+ currentOrderedListItems = [];
+ inOrderedList = false;
+ }
+ };
+
+ const flushParagraph = () => {
+ if (currentParagraph.length > 0) {
+ nodes.push(schema.nodes.paragraph.create({}, currentParagraph));
+ currentParagraph = [];
+ }
+ };
+
+ const processHeader = (line: string) => {
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
+ if (headerMatch) {
+ const level = Math.min(headerMatch[1].length, 6); // Cap at h6
+ const textContent = headerMatch[2];
+ flushParagraph();
+ nodes.push(schema.nodes.heading.create({ level }, processBoldText(textContent)));
+ plainText += textContent + '\n';
+ return true;
+ }
+ return false;
+ };
+
+ while (i < lines.length) {
+ const line = lines[i].trim();
+ if (line) {
+ if (processHeader(line)) {
+ flushListItems();
+ flushParagraph();
+ } else if (line.startsWith('- ')) {
+ flushParagraph();
+ if (!inBulletList) {
+ flushListItems();
+ inBulletList = true;
+ }
+ const textContent = line.replace('- ', '');
+ currentListItems.push(schema.nodes.list_item.create({}, schema.nodes.paragraph.create({}, processBoldText(textContent))));
+ plainText += textContent + '\n';
+ } else if (/^\d+\.\s+/.test(line)) {
+ flushParagraph();
+ if (!inOrderedList) {
+ flushListItems();
+ inOrderedList = true;
+ }
+ const textContent = line.replace(/^\d+\.\s+/, '');
+ currentOrderedListItems.push(schema.nodes.list_item.create({}, schema.nodes.paragraph.create({}, processBoldText(textContent))));
+ plainText += textContent + '\n';
+ } else {
+ flushListItems();
+ currentParagraph = currentParagraph.concat(processBoldText(line));
+ plainText += line + '\n';
+ }
+ } else {
+ flushListItems();
+ flushParagraph();
+ nodes.push(schema.nodes.paragraph.create());
+ plainText += '\n';
+ }
+ i++;
+ }
+ flushListItems();
+ flushParagraph();
+
+ const doc = schema.nodes.doc.create({}, nodes);
+ return { doc, plainText: plainText.trim() };
+};
+
+export class GPTTutorialTool extends BaseTool<typeof generateTutorialNodeToolParams> {
+ private _createDocInDash: (doc: parsedDoc) => Doc | undefined;
+
+ constructor(createDocInDash: (doc: parsedDoc) => Doc | undefined) {
+ super(generateTutorialNodeToolInfo);
+
+ this._createDocInDash = createDocInDash;
+ }
+
+ async execute(args: ParametersType<typeof generateTutorialNodeToolParams>): Promise<Observation[]> {
+ const chunkId = uuidv4();
+ try {
+ const query = (args.query || '').trim();
+ if (!query) {
+ return [{ type: 'text', text: `<chunk chunk_id="${chunkId}" chunk_type="error">Please provide a query.</chunk>` }];
+ }
+ const markdown = await gptTutorialAPICall(query);
+ const { doc, plainText } = applyFormatting(markdown);
+
+ // Build the ProseMirror‐in‐JSON + plain-text for RichTextField
+ const rtfData = {
+ doc: (doc as any).toJSON ? (doc as any).toJSON() : doc,
+ selection: { type: 'text', anchor: 0, head: 0 },
+ storedMarks: [],
+ };
+ const rtf = new RichTextField(JSON.stringify(rtfData), plainText);
+
+ // Create and show the TextDocument directly:
+ const formattedDoc = Docs.Create.TextDocument(rtf, {
+ title: 'Tutorial Node',
+ _width: 600,
+ _layout_fitWidth: true,
+ _layout_autoHeight: true,
+ text_fontSize: '16px',
+ });
+ DocumentViewInternal.addDocTabFunc(formattedDoc, OpenWhere.addRight);
+
+ // If user asked about linking/pinning/presentation, also fire the in-app tutorial:
+ const q = query.toLowerCase();
+ if (q.includes('link')) {
+ Doc.IsInfoUIDisabled = false;
+ CollectionFreeFormView.showTutorial('links');
+ } else if (q.includes('presentation')) {
+ Doc.IsInfoUIDisabled = false;
+ CollectionFreeFormView.showTutorial('presentation');
+ } else if (q.includes('pin')) {
+ Doc.IsInfoUIDisabled = false;
+ CollectionFreeFormView.showTutorial('pins');
+ }
+
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="tutorial_node_creation">Created tutorial node with ID ${formattedDoc[Id]}.</chunk>`,
+ },
+ ];
+ } catch (error) {
+ return [
+ {
+ type: 'text',
+ text: `<chunk chunk_id="${chunkId}" chunk_type="error">Error generating tutorial node: ${error}</chunk>`,
+ },
+ ];
+ }
+ }
+}