diff options
-rw-r--r-- | src/client/views/nodes/ChatBox/Agent.ts | 4 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/ChatBox.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/MessageComponent.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/prompts.ts | 50 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/tools/CollectionTool.ts | 0 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/tools/CreateCollectionTool.ts | 35 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/tools/GetDocsTool.ts | 29 | ||||
-rw-r--r-- | src/client/views/nodes/ChatBox/tools/NoTool.ts | 18 |
8 files changed, 142 insertions, 7 deletions
diff --git a/src/client/views/nodes/ChatBox/Agent.ts b/src/client/views/nodes/ChatBox/Agent.ts index bada4b146..a3b1d083c 100644 --- a/src/client/views/nodes/ChatBox/Agent.ts +++ b/src/client/views/nodes/ChatBox/Agent.ts @@ -5,6 +5,7 @@ import { XMLParser, XMLBuilder } from 'fast-xml-parser'; import { WikipediaTool } from './tools/WikipediaTool'; import { CalculateTool } from './tools/CalculateTool'; import { RAGTool } from './tools/RAGTool'; +import { NoTool } from './tools/NoTool'; import { Vectorstore } from './vectorstore/VectorstoreUpload'; import { ChatCompletionAssistantMessageParam, ChatCompletionMessageParam } from 'openai/resources'; import dotenv from 'dotenv'; @@ -27,6 +28,7 @@ export class Agent { wikipedia: new WikipediaTool(), calculate: new CalculateTool(), rag: new RAGTool(this.vectorstore, summaries), + no_tool: new NoTool(), }; } @@ -132,7 +134,7 @@ export class Agent { for (const paramName in tool.parameters) { if (actionInput[paramName] !== undefined) { args[paramName] = actionInput[paramName]; - } else { + } else if (tool.parameters[paramName].required === 'true') { throw new Error(`Missing required parameter '${paramName}' for action '${action}'`); } } diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx index 8b4a7bd0a..e3a164b3e 100644 --- a/src/client/views/nodes/ChatBox/ChatBox.tsx +++ b/src/client/views/nodes/ChatBox/ChatBox.tsx @@ -54,7 +54,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { constructor(props: FieldViewProps) { super(props); makeObservable(this); - this.history = [{ role: ASSISTANT_ROLE.ASSISTANT, text_content: 'Welcome to the Document Analyser Assistant! Link a document or ask questions to get started.' }]; this.openai = this.initializeOpenAI(); if (StrCast(this.dataDoc.vectorstore_id) == '') { console.log('new_id'); @@ -278,6 +277,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } catch (e) { console.error('Failed to parse history from dataDoc:', e); } + } else { + this.history = [{ role: ASSISTANT_ROLE.ASSISTANT, text_content: 'Welcome to the Document Analyser Assistant! Link a document or ask questions to get started.' }]; } reaction( () => { diff --git a/src/client/views/nodes/ChatBox/MessageComponent.tsx b/src/client/views/nodes/ChatBox/MessageComponent.tsx index 9f3dee990..fb4a56bc3 100644 --- a/src/client/views/nodes/ChatBox/MessageComponent.tsx +++ b/src/client/views/nodes/ChatBox/MessageComponent.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { observer } from 'mobx-react'; +import ReactMarkdown from 'react-markdown'; import { AssistantMessage, Citation } from './types'; interface MessageComponentProps { @@ -13,7 +14,7 @@ interface MessageComponentProps { const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message, index, onFollowUpClick, onCitationClick, updateMessageCitations }) { const renderContent = (content: string) => { if (!message.citations || message.citations.length === 0) { - return content; + return <ReactMarkdown>{content}</ReactMarkdown>; } const parts = []; @@ -22,9 +23,10 @@ const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message message.citations.forEach((citation, idx) => { const location = citation.text_location; const textBefore = content.slice(lastIndex, location); + parts.push(<ReactMarkdown key={`md-${idx}`}>{textBefore}</ReactMarkdown>); const citationButton = ( <button - key={idx} + key={`citation-${idx}`} className="citation-button" onClick={() => onCitationClick(citation)} style={{ @@ -46,11 +48,11 @@ const MessageComponentBox: React.FC<MessageComponentProps> = function ({ message {idx + 1} </button> ); - parts.push(textBefore, citationButton); + parts.push(citationButton); lastIndex = location; }); - parts.push(content.slice(lastIndex)); + parts.push(<ReactMarkdown key="md-last">{content.slice(lastIndex)}</ReactMarkdown>); return parts; }; diff --git a/src/client/views/nodes/ChatBox/prompts.ts b/src/client/views/nodes/ChatBox/prompts.ts index c011d65b7..d5eb99cb2 100644 --- a/src/client/views/nodes/ChatBox/prompts.ts +++ b/src/client/views/nodes/ChatBox/prompts.ts @@ -19,6 +19,8 @@ export function getReactPrompt(tools: Tool[], chatHistory: string): string { Your available actions are: ***** ${toolDescriptions} + ***** + no_tool: Use this when no external tool or action is required to answer the question. ********** Example: You will be called with: @@ -75,12 +77,58 @@ export function getReactPrompt(tools: Tool[], chatHistory: string): string { </answer> </step3> ********** + Example: +You will be called with: +<query>What is 2 + 2?</query> + +You will then output: +<step1> + <thought>This is a simple arithmetic question that doesn't require any external tool.</thought> + <action>no_tool</action> +</step1> + +THEN PAUSE AND DO NOT OUTPUT ANYTHING. + +You will be called again with this: +<action_rules> + { + "no_tool": { + "name": "no_tool", + "description": "Use when no external tool or action is required", + "parameters": [] + } + } +</action_rules> + +You will then output: +<step2> + <action_input></action_input> +</step2> + +THEN PAUSE AND DO NOT OUTPUT ANYTHING. + +You will then be called again with this: +<observation>No tool used. Proceed with answering the question.</observation> + +You then output: +<step3> + <answer> + 2 + 2 equals 4. + <follow_up_questions> + <question>What is 3 + 3?</question> + <question>Can you explain the concept of addition?</question> + <question>What is 2 * 2?</question> + </follow_up_questions> + </answer> +</step3> + ********** Here is the history of your conversation with the user (all loop steps are ommitted, so it is just the user query and final answer): ${chatHistory} Use context from the past conversation if necessary. ********** If the response is inadequate, repeat the loop, either trying a different tool or changing the parameters for the action input. - + ********** + !!!IMPORTANT Only use tools when they are absolutely necessary to answer the question. If you have enough information or knowledge to answer the question without using a tool, use the "no_tool" action instead. !!!IMPORTANT When you have an Answer, Write your entire response inside an <answer> element (which itself should be inside the step element for the current step). After you finish the answer, provide an array of 3 follow-up questions inside a <follow_up_questions> array. These should relate to the query and the response and should aim to help the user better understand whatever they are looking for. ********** !!!IMPORTANT Every response, provide in full parsable and valid XML with the root element being the step number (e.g. <step1>), iterated every time you output something new. diff --git a/src/client/views/nodes/ChatBox/tools/CollectionTool.ts b/src/client/views/nodes/ChatBox/tools/CollectionTool.ts deleted file mode 100644 index e69de29bb..000000000 --- a/src/client/views/nodes/ChatBox/tools/CollectionTool.ts +++ /dev/null diff --git a/src/client/views/nodes/ChatBox/tools/CreateCollectionTool.ts b/src/client/views/nodes/ChatBox/tools/CreateCollectionTool.ts new file mode 100644 index 000000000..26ac0d7cc --- /dev/null +++ b/src/client/views/nodes/ChatBox/tools/CreateCollectionTool.ts @@ -0,0 +1,35 @@ +import { DocCast } from '../../../../../fields/Types'; +import { DocServer } from '../../../../DocServer'; +import { Docs } from '../../../../documents/Documents'; +import { DocumentView } from '../../DocumentView'; +import { OpenWhere } from '../../OpenWhere'; +import { BaseTool } from './BaseTool'; + +export class GetDocsContentTool extends BaseTool<{ title: string; document_ids: string[] }> { + private _docView: DocumentView; + constructor(docView: DocumentView) { + super( + 'retrieveDocs', + 'Retrieves the contents of all Documents that the user is interacting with in Dash ', + { + title: { + type: 'string', + description: 'the title of the collection that you will be making', + required: 'true', + }, + }, + 'Provide a mathematical expression to calculate that would work with JavaScript eval().', + 'Runs a calculation and returns the number - uses JavaScript so be sure to use floating point syntax if necessary' + ); + this._docView = docView; + } + + async execute(args: { title: string; document_ids: string[] }): Promise<any> { + // Note: Using eval() can be dangerous. Consider using a safer alternative. + const docs = args.document_ids.map(doc_id => DocCast(DocServer.GetCachedRefField(doc_id))); + const collection = Docs.Create.FreeformDocument(docs, { title: args.title }); + this._docView._props.addDocTab(collection, OpenWhere.addRight); //in future, create popup prompting user where to add + return [{ type: 'text', text: 'Collection created in Dash called ' + args.title }]; + } +} +//export function create_collection(docView: DocumentView, document_ids: string[], title: string): string {} diff --git a/src/client/views/nodes/ChatBox/tools/GetDocsTool.ts b/src/client/views/nodes/ChatBox/tools/GetDocsTool.ts new file mode 100644 index 000000000..f970ca8ee --- /dev/null +++ b/src/client/views/nodes/ChatBox/tools/GetDocsTool.ts @@ -0,0 +1,29 @@ +import { DocCast } from '../../../../../fields/Types'; +import { DocServer } from '../../../../DocServer'; +import { Docs } from '../../../../documents/Documents'; +import { DocumentView } from '../../DocumentView'; +import { OpenWhere } from '../../OpenWhere'; +import { BaseTool } from './BaseTool'; + +export class GetDocsTool extends BaseTool<{ title: string; document_ids: string[] }> { + private _docView: DocumentView; + constructor(docView: DocumentView) { + super( + 'retrieveDocs', + 'Retrieves the contents of all Documents that the user is interacting with in Dash', + {}, + 'No need to provide anything. Just run the tool and it will retrieve the contents of all Documents that the user is interacting with in Dash.', + 'Returns the the documents in Dash in JSON form. This will include the title of the document, the location in the FreeFormDocument, and the content of the document, any applicable data fields, the layout of the document, etc.' + ); + this._docView = docView; + } + + async execute(args: { title: string; document_ids: string[] }): Promise<any> { + // Note: Using eval() can be dangerous. Consider using a safer alternative. + const docs = args.document_ids.map(doc_id => DocCast(DocServer.GetCachedRefField(doc_id))); + const collection = Docs.Create.FreeformDocument(docs, { title: args.title }); + this._docView._props.addDocTab(collection, OpenWhere.addRight); //in future, create popup prompting user where to add + return [{ type: 'text', text: 'Collection created in Dash called ' + args.title }]; + } +} +//export function create_collection(docView: DocumentView, document_ids: string[], title: string): string {} diff --git a/src/client/views/nodes/ChatBox/tools/NoTool.ts b/src/client/views/nodes/ChatBox/tools/NoTool.ts new file mode 100644 index 000000000..1f0830a77 --- /dev/null +++ b/src/client/views/nodes/ChatBox/tools/NoTool.ts @@ -0,0 +1,18 @@ +// tools/NoTool.ts +import { BaseTool } from './BaseTool'; + +export class NoTool extends BaseTool<{}> { + constructor() { + super( + 'no_tool', + 'Use this when no external tool or action is required to answer the question.', + {}, + 'When using the "no_tool" action, simply provide an empty <action_input> element. The observation will always be "No tool used. Proceed with answering the question."', + 'Use when no external tool or action is required to answer the question.' + ); + } + + async execute(args: {}): Promise<any> { + return [{ type: 'text', text: 'No tool used. Proceed with answering the question.' }]; + } +} |