aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/ChatBox/Agent.ts83
-rw-r--r--src/client/views/nodes/ChatBox/ChatBot.ts16
-rw-r--r--src/client/views/nodes/ChatBox/ChatBox.tsx27
-rw-r--r--src/client/views/nodes/ChatBox/MessageComponent.tsx8
-rw-r--r--src/client/views/nodes/ChatBox/prompts.ts5
5 files changed, 77 insertions, 62 deletions
diff --git a/src/client/views/nodes/ChatBox/Agent.ts b/src/client/views/nodes/ChatBox/Agent.ts
index 4c2838540..355acb19f 100644
--- a/src/client/views/nodes/ChatBox/Agent.ts
+++ b/src/client/views/nodes/ChatBox/Agent.ts
@@ -18,7 +18,7 @@ export class Agent {
private summaries: string;
constructor(private vectorstore: Vectorstore) {
- this.client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
+ this.client = new OpenAI({ apiKey: process.env.OPENAI_KEY, dangerouslyAllowBrowser: true });
this.summaries = this.vectorstore ? this.vectorstore.getSummaries() : 'No documents available.';
this.tools = {
wikipedia: new WikipediaTool(),
@@ -40,7 +40,7 @@ export class Agent {
return history;
}
- async askAgent(question: string, maxTurns: number = 5): Promise<string> {
+ async askAgent(question: string, maxTurns: number = 8): Promise<string> {
console.log(`Starting query: ${question}`);
this.messages.push({ role: 'user', content: question });
const chatHistory = this.formatChatHistory();
@@ -51,6 +51,10 @@ export class Agent {
this.interMessages.push({ role: 'assistant', content: `<query>${question}</query>` });
+ const parser = new XMLParser();
+ const builder = new XMLBuilder();
+ let currentAction: string | undefined;
+
for (let i = 0; i < maxTurns; i++) {
console.log(`Turn ${i + 1}/${maxTurns}`);
@@ -58,42 +62,58 @@ export class Agent {
console.log(`Bot response: ${result}`);
this.interMessages.push({ role: 'assistant', content: result });
+ let parsedResult;
try {
- const parser = new XMLParser();
- const parsedResult = parser.parse(result);
- const step = parsedResult[`step${i + 1}`];
-
- if (step.thought) console.log(`Thought: ${step.thought}`);
- if (step.action) {
- console.log(`Action: ${step.action}`);
- const action = step.action;
- const actionRules = new XMLBuilder().build({
- action_rules: this.tools[action].getActionRule(),
- });
- this.interMessages.push({ role: 'user', content: actionRules });
- }
- if (step.action_input) {
- const actionInput = new XMLBuilder().build({ action_input: step.action_input });
+ parsedResult = parser.parse(result);
+ } catch (error) {
+ console.log('Error: Invalid XML response from bot');
+ return '<error>Invalid response format.</error>';
+ }
+
+ const step = parsedResult[Object.keys(parsedResult)[0]];
+
+ for (const key in step) {
+ if (key === 'thought') {
+ console.log(`Thought: ${step[key]}`);
+ } else if (key === 'action') {
+ currentAction = step[key] as string;
+ console.log(`Action: ${currentAction}`);
+ if (this.tools[currentAction]) {
+ const nextPrompt = [
+ {
+ type: 'text',
+ text: builder.build({ action_rules: this.tools[currentAction].getActionRule() }),
+ },
+ ];
+ this.interMessages.push({ role: 'assistant', content: nextPrompt });
+ break;
+ } else {
+ console.log('Error: No valid action');
+ }
+ } else if (key === 'action_input') {
+ const actionInput = builder.build({ action_input: step[key] });
console.log(`Action input: ${actionInput}`);
- try {
- const observation = await this.processAction(step.action, step.action_input);
- const nextPrompt = [{ type: 'text', text: '<observation>' }, ...observation, { type: 'text', text: '</observation>' }];
- this.interMessages.push({ role: 'user', content: nextPrompt });
- } catch (e) {
- console.error(`Error processing action: ${e}`);
- return `<error>${e}</error>`;
+ if (currentAction) {
+ try {
+ const observation = await this.processAction(currentAction, step[key]);
+ const nextPrompt = [{ type: 'text', text: '<observation>' }, ...observation, { type: 'text', text: '</observation>' }];
+ this.interMessages.push({ role: 'assistant', content: nextPrompt });
+ break;
+ } catch (error) {
+ console.log(`Error processing action: ${error}`);
+ return `<error>${error}</error>`;
+ }
+ } else {
+ console.log('Error: Action input without a valid action');
+ return '<error>Action input without a valid action</error>';
}
- }
- if (step.answer) {
+ } else if (key === 'answer') {
console.log('Answer found. Ending query.');
- const answerContent = new XMLBuilder().build({ answer: step.answer });
+ const answerContent = builder.build({ answer: step[key] });
this.messages.push({ role: 'assistant', content: answerContent });
this.interMessages = [];
return answerContent;
}
- } catch (e) {
- console.error('Error: Invalid XML response from bot');
- return '<error>Invalid response format.</error>';
}
}
@@ -102,8 +122,9 @@ export class Agent {
}
private async execute(): Promise<string> {
+ console.log('Messages: ' + this.interMessages);
const completion = await this.client.chat.completions.create({
- model: 'gpt-4',
+ model: 'gpt-4o',
messages: this.interMessages as ChatCompletionMessageParam[],
temperature: 0,
});
diff --git a/src/client/views/nodes/ChatBox/ChatBot.ts b/src/client/views/nodes/ChatBox/ChatBot.ts
deleted file mode 100644
index 8b5e0982c..000000000
--- a/src/client/views/nodes/ChatBox/ChatBot.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Agent } from './Agent';
-import { Vectorstore } from './vectorstore/VectorstoreUpload';
-import dotenv from 'dotenv';
-dotenv.config();
-
-export class ChatBot {
- private agent: Agent;
-
- constructor(vectorstore: Vectorstore) {
- this.agent = new Agent(vectorstore);
- }
-
- async ask(question: string): Promise<string> {
- return await this.agent.askAgent(question);
- }
-}
diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx
index 3ecb2d340..2ce1ebdd2 100644
--- a/src/client/views/nodes/ChatBox/ChatBox.tsx
+++ b/src/client/views/nodes/ChatBox/ChatBox.tsx
@@ -16,7 +16,7 @@ import { ASSISTANT_ROLE, AssistantMessage, AI_Document, convertToAIDocument, Cit
import { Vectorstore } from './vectorstore/VectorstoreUpload';
import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
-import { ChatBot } from './ChatBot';
+import { Agent } from './Agent';
import dotenv from 'dotenv';
dotenv.config();
@@ -34,7 +34,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
private documents: AI_Document[] = [];
private _oldWheel: any;
private vectorstore: Vectorstore;
- private chatbot: ChatBot; // Add the ChatBot instance
+ private agent: Agent; // Add the ChatBot instance
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ChatBox, fieldKey);
@@ -48,7 +48,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.openai = this.initializeOpenAI();
this.getOtherDocs();
this.vectorstore = new Vectorstore();
- this.chatbot = new ChatBot(this.vectorstore); // Initialize the ChatBot
+ this.agent = new Agent(this.vectorstore); // Initialize the Agent
reaction(
() => this.history.map((msg: AssistantMessage) => ({ role: msg.role, text: msg.text, follow_up_questions: msg.follow_up_questions, citations: msg.citations })),
@@ -58,6 +58,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
}
+ @action
getOtherDocs = async () => {
const visible_docs = (CollectionFreeFormDocumentView.from(this._props.DocumentView?.())?._props.parent as CollectionFreeFormView)?.childDocs
.filter(doc => doc != this.Document)
@@ -76,6 +77,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
doc['ai_document'] = document_json;
}
});
+ this.isInitializing = false;
};
@action
@@ -120,7 +122,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.history.push({ role: ASSISTANT_ROLE.USER, text: trimmedText });
});
this.isLoading = true;
- const response = await this.chatbot.ask(trimmedText); // Use the chatbot to get the response
+ const response = await this.agent.askAgent(trimmedText); // Use the chatbot to get the response
runInAction(() => {
this.history.push(this.parseAssistantResponse(response));
});
@@ -142,8 +144,21 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const answerElement = xmlDoc.querySelector('answer');
const followUpQuestionsElement = xmlDoc.querySelector('follow_up_questions');
- const text = answerElement ? answerElement.innerHTML || '' : ''; // Use innerHTML to preserve citation tags
- const followUpQuestions = followUpQuestionsElement ? Array.from(followUpQuestionsElement.querySelectorAll('question')).map(q => q.textContent || '') : [];
+ let text = '';
+ let followUpQuestions: string[] = [];
+
+ if (answerElement) {
+ // Remove the follow_up_questions element from the answer
+ const followUpElement = answerElement.querySelector('follow_up_questions');
+ if (followUpElement) {
+ followUpElement.remove();
+ }
+ text = answerElement.innerHTML.trim();
+ }
+
+ if (followUpQuestionsElement) {
+ followUpQuestions = Array.from(followUpQuestionsElement.querySelectorAll('question')).map(q => q.textContent || '');
+ }
return {
role: ASSISTANT_ROLE.ASSISTANT,
diff --git a/src/client/views/nodes/ChatBox/MessageComponent.tsx b/src/client/views/nodes/ChatBox/MessageComponent.tsx
index 1baf6d7d5..91671a24a 100644
--- a/src/client/views/nodes/ChatBox/MessageComponent.tsx
+++ b/src/client/views/nodes/ChatBox/MessageComponent.tsx
@@ -12,8 +12,7 @@ interface MessageComponentProps {
}
const MessageComponent: React.FC<MessageComponentProps> = function ({ message, index, onFollowUpClick, onCitationClick, updateMessageCitations }) {
- const LinkRenderer = ({ children }: { children: React.ReactNode }) => {
- const text = children as string;
+ const renderContent = (text: string) => {
const citationRegex = /<citation chunk_id="([^"]*)" type="([^"]*)">([^<]*)<\/citation>/g;
const parts = [];
let lastIndex = 0;
@@ -47,7 +46,6 @@ const MessageComponent: React.FC<MessageComponentProps> = function ({ message, i
parts.push(text.slice(lastIndex));
- // Update the message's citations in the ChatBox's history
updateMessageCitations(index, citations);
return <>{parts}</>;
@@ -55,9 +53,7 @@ const MessageComponent: React.FC<MessageComponentProps> = function ({ message, i
return (
<div className={`message ${message.role}`}>
- <div>
- <LinkRenderer>{message.text}</LinkRenderer>
- </div>
+ <div>{renderContent(message.text)}</div>
{message.follow_up_questions && message.follow_up_questions.length > 0 && (
<div className="follow-up-questions">
<h4>Follow-up Questions:</h4>
diff --git a/src/client/views/nodes/ChatBox/prompts.ts b/src/client/views/nodes/ChatBox/prompts.ts
index 8835265e4..ffea13788 100644
--- a/src/client/views/nodes/ChatBox/prompts.ts
+++ b/src/client/views/nodes/ChatBox/prompts.ts
@@ -6,12 +6,12 @@ export function getReactPrompt(tools: Tool[], chatHistory: string): string {
const toolDescriptions = tools.map(tool => `${tool.name}:\n${tool.briefSummary}`).join('\n*****\n');
return `
- You run in a loop of Thought, Action, PAUSE, Action Input, Pause, Observation.
+ You run in a loop of Thought, Action, (PAUSE), Action Input, (PAUSE), Observation.
(this Thought/Action/PAUSE/Action Input/PAUSE/Observation can repeat N times)
Contain each stage of the loop within an XML element that specifies the stage type (e.g. <thought>content of the thought</thought>).
At the end of the loop, you output an Answer with the answer content contained within an XML element with an <answer> tag. At the end of the answer should be an array of 3 potential follow-up questions for the user to ask you next, contained within a <follow_up_questions> key.
Use <thought> to describe your thoughts about the question you have been asked.
- Use <action> to specify run one of the actions available to you - then return a </pause> element.
+ Use <action> to specify run one of the actions available to you.
Then, you will be provided with action rules within an <action_rules> element that specifies how you should structure the input to the action and what the output of that action will look like - then return another </pause> element.
Then, provide within an <action_input> element each parameter, with parameter names as element tags themselves with their values inside, following the structure defined in the action rules.
Observation, in an <observation> element will be the result of running those actions.
@@ -28,7 +28,6 @@ export function getReactPrompt(tools: Tool[], chatHistory: string): string {
<step1>
<thought>I should look up France on Wikipedia</thought>
<action>wikipedia</action>
- <pause/>
</step1>
You will be called again with this: