aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ChatBox/ChatBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ChatBox/ChatBox.tsx')
-rw-r--r--src/client/views/nodes/ChatBox/ChatBox.tsx304
1 files changed, 75 insertions, 229 deletions
diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx
index b986c7393..9f4e6f07e 100644
--- a/src/client/views/nodes/ChatBox/ChatBox.tsx
+++ b/src/client/views/nodes/ChatBox/ChatBox.tsx
@@ -19,91 +19,82 @@ import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import './ChatBox.scss';
import MessageComponent from './MessageComponent';
-import { ANNOTATION_LINK_TYPE, ASSISTANT_ROLE, AssistantMessage, DOWNLOAD_TYPE } from './types';
+import { ASSISTANT_ROLE, AssistantMessage, AI_Document, convertToAIDocument } from './types';
import { Annotation } from 'mobx/dist/internal';
import { FormEvent } from 'react';
+import { url } from 'inspector';
+import { Vectorstore } from './vectorstore/VectorstoreUpload';
+import { DocumentView } from '../DocumentView';
+import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
@observer
export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
- @observable modalStatus = false;
- @observable currentFile = { url: '' };
@observable history: AssistantMessage[] = [];
@observable.deep current_message: AssistantMessage | undefined = undefined;
@observable isLoading: boolean = false;
@observable isInitializing: boolean = true;
- @observable expandedLogIndex: number | null = null;
+ @observable expandedScratchpadIndex: number | null = null;
@observable linked_docs_to_add: Doc[] = [];
@observable inputValue: string = '';
private openai: OpenAI;
- private assistantID: string = '';
- private threadID: string = '';
+ private documents: AI_Document[] = [];
private _oldWheel: any;
- private vectorStoreID: string = '';
- private mathJaxConfig: any;
- private linkedCsvIDs: string[] = [];
+ private vectorstore: Vectorstore;
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ChatBox, fieldKey);
}
+
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
this.openai = this.initializeOpenAI();
this.history = [{ role: ASSISTANT_ROLE.ASSISTANT, text: 'Welcome to the Document Analyser Assistant! Link a document or ask questions to get started.' }];
- this.threadID = StrCast(this.dataDoc.thread_id);
- this.assistantID = StrCast(this.dataDoc.assistant_id);
- this.vectorStoreID = StrCast(this.dataDoc.vector_store_id);
this.openai = this.initializeOpenAI();
- if (this.assistantID === '' || this.threadID === '' || this.vectorStoreID === '') {
- this.createAssistant();
- } else {
- this.retrieveCsvUrls();
- this.isInitializing = false;
- }
- this.mathJaxConfig = {
- loader: { load: ['input/asciimath'] },
- tex: {
- inlineMath: [
- ['$', '$'],
- ['\\(', '\\)'],
- ],
- displayMath: [
- ['$$', '$$'],
- ['[', ']'],
- ],
- },
- };
+ this.getLinkedDocs();
+ this.vectorstore = new Vectorstore();
+
reaction(
- () => this.history.map((msg: AssistantMessage) => ({ role: msg.role, text: msg.text, image: msg.image, tool_logs: msg.tool_logs })),
+ () => this.history.map((msg: AssistantMessage) => ({ role: msg.role, text: msg.text, follow_up_questions: msg.follow_up_questions, citations: msg.citations })),
serializableHistory => {
this.dataDoc.data = JSON.stringify(serializableHistory);
}
);
}
- @action
- toggleToolLogs = (index: number) => {
- this.expandedLogIndex = this.expandedLogIndex === index ? null : index;
- };
+ getLinkedDocs = async () => {
+ const visual_docs = (CollectionFreeFormDocumentView.from(this._props.DocumentView?.())?._props.parent as CollectionFreeFormView)?.childDocs.filter(doc => doc != this.Document);
+ console.log('All Docs:', visual_docs);
- retrieveCsvUrls() {
- const linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.Document)
- .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
- .map(d => DocCast(d?.annotationOn, d))
- .filter(d => d);
+ visual_docs?.forEach(async doc => {
+ const local_file_path: string = CsvCast(doc.data, PDFCast(doc.data)).url?.pathname;
- linkedDocs.forEach(doc => {
- const aiFieldId = StrCast(doc[this.Document[Id] + '_ai_field_id']);
- if (CsvCast(doc.data)) {
- this.linkedCsvIDs.push(StrCast(aiFieldId));
- console.log(this.linkedCsvIDs);
+ if (local_file_path) {
+ const { document_json } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
+ const ai_document: AI_Document = convertToAIDocument(document_json);
+ this.documents.push(ai_document);
+ await this.vectorstore.addDocument(ai_document);
+ doc['ai_document'] = document_json;
}
});
- }
+ };
+
+ @action
+ uploadNewDocument = async (newDoc: Doc) => {
+ const local_file_path: string = CsvCast(newDoc.data, PDFCast(newDoc.data)).url.pathname;
+ const { document_json } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
+ this.documents.push(...document_json.map(convertToAIDocument));
+ newDoc['ai_document'] = document_json;
+ };
+
+ @action
+ toggleToolLogs = (index: number) => {
+ this.expandedScratchpadIndex = this.expandedScratchpadIndex === index ? null : index;
+ };
initializeOpenAI() {
- //console.log(process.env._CLIENT_OPENAI_KEY);
const configuration: ClientOptions = {
apiKey: process.env.OPENAI_KEY,
dangerouslyAllowBrowser: true,
@@ -118,50 +109,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
- createAssistant = async () => {
- this.isInitializing = true;
- try {
- const vectorStore = await this.openai.beta.vectorStores.create({
- name: 'Vector Store for Assistant',
- });
- const assistant = await this.openai.beta.assistants.create({
- name: 'Document Analyser Assistant',
- instructions: `
- You will analyse documents with which you are provided. You will answer questions and provide insights based on the information in the documents.
- For writing math formulas:
- You have a MathJax render environment.
- - Write all in-line equations within a single dollar sign, $, to render them as TeX (this means any time you want to use a dollar sign to represent a dollar sign itself, you must escape it with a backslash: "$");
- - Use a double dollar sign, $$, to render equations on a new line;
- Example: $$x^2 + 3x$$ is output for "x² + 3x" to appear as TeX.`,
- model: 'gpt-4-turbo',
- tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
- tool_resources: {
- file_search: {
- vector_store_ids: [vectorStore.id],
- },
- code_interpreter: {
- file_ids: this.linkedCsvIDs,
- },
- },
- });
- const thread = await this.openai.beta.threads.create();
-
- runInAction(() => {
- this.dataDoc.assistant_id = assistant.id;
- this.dataDoc.thread_id = thread.id;
- this.dataDoc.vector_store_id = vectorStore.id;
- this.assistantID = assistant.id;
- this.threadID = thread.id;
- this.vectorStoreID = vectorStore.id;
- this.isInitializing = false;
- });
- } catch (error) {
- console.error('Initialization failed:', error);
- this.isInitializing = false;
- }
- };
-
- @action
askGPT = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
this.inputValue = '';
@@ -222,111 +169,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// // }
// };
- downloadToComputer = (url: string, fileName: string) => {
- fetch(url, { method: 'get', mode: 'no-cors', referrerPolicy: 'no-referrer' })
- .then(res => res.blob())
- .then(res => {
- const aElement = document.createElement('a');
- aElement.setAttribute('download', fileName);
- const href = URL.createObjectURL(res);
- aElement.href = href;
- aElement.setAttribute('target', '_blank');
- aElement.click();
- URL.revokeObjectURL(href);
- });
- };
-
- createDocumentInDash = async (url: string) => {
- const fileSuffix = url.substring(url.lastIndexOf('.') + 1);
- console.log(fileSuffix);
- let doc: Doc | null = null;
- switch (fileSuffix) {
- case 'pdf':
- doc = DocCast(await DocUtils.DocumentFromType('pdf', url, {}));
- break;
- case 'csv':
- doc = DocCast(await DocUtils.DocumentFromType('csv', url, {}));
- break;
- case 'png':
- case 'jpg':
- case 'jpeg':
- doc = DocCast(await DocUtils.DocumentFromType('image', url, {}));
- break;
- default:
- console.error('Unsupported file type:', fileSuffix);
- break;
- }
- if (doc) {
- doc && this._props.addDocument?.(doc);
- //add to overlay
- await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, () => {});
- }
- };
-
- downloadFile = async (fileInfo: string, downloadType: DOWNLOAD_TYPE) => {
- try {
- console.log(fileInfo);
- const [fileId, fileName] = fileInfo.split(/!!!/);
- const { file_path: filePath } = await Networking.PostToServer('/downloadFileFromOpenAI', { file_id: fileId, file_name: fileName });
- const fileLink = CsvCast(new CsvField(filePath)).url.href;
- if (downloadType === DOWNLOAD_TYPE.DASH) {
- this.createDocumentInDash(fileLink);
- } else {
- this.downloadToComputer(fileLink, fileName);
- }
- } catch (error) {
- console.error('Error downloading file:', error);
- }
- };
-
- handleDownloadToDevice = () => {
- this.downloadFile(this.currentFile.url, DOWNLOAD_TYPE.DEVICE);
- this.modalStatus = false; // Close the modal after the action
- this.currentFile = { url: '' }; // Reset the current file
- };
-
- handleAddToDash = () => {
- // Assuming `downloadFile` is a method that handles adding to Dash
- this.downloadFile(this.currentFile.url, DOWNLOAD_TYPE.DASH);
- this.modalStatus = false; // Close the modal after the action
- this.currentFile = { url: '' }; // Reset the current file
- };
-
- renderModal = () => {
- if (!this.modalStatus) return null;
-
- return (
- <div className="modal">
- <div className="modal-content">
- <h4>File Actions</h4>
- <p>Choose an action for the file:</p>
- <button type="button" onClick={this.handleDownloadToDevice}>
- Download to Device
- </button>
- <button type="button" onClick={this.handleAddToDash}>
- Add to Dash
- </button>
- <button
- type="button"
- onClick={() => {
- this.modalStatus = false;
- }}>
- Cancel
- </button>
- </div>
- </div>
- );
- };
- @action
- showModal = () => {
- this.modalStatus = true;
- };
-
- @action
- setCurrentFile = (file: { url: string }) => {
- this.currentFile = file;
- };
-
componentDidMount() {
this._props.setContentViewBox?.(this);
if (this.dataDoc.data) {
@@ -337,9 +179,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
...storedHistory.map((msg: AssistantMessage) => ({
role: msg.role,
text: msg.text,
- quote: msg.quote,
- tool_logs: msg.tool_logs,
- image: msg.image,
+ follow_up_questions: msg.follow_up_questions,
+ citations: msg.citations,
}))
);
});
@@ -355,7 +196,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.filter(d => d);
return linkedDocs;
},
-
linked => this.linked_docs_to_add.push(...linked.filter(linkedDoc => !this.linked_docs_to_add.includes(linkedDoc)))
);
@@ -370,7 +210,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
if ((change as any).addedCount > 0) {
// maybe check here if its already in the urls datadoc array so doesn't add twice
console.log((change as any).added as Doc[]);
- this.uploadLinks((change as any).added as Doc[]);
+ ((change as any).added as Doc[]).forEach(doc => {
+ this.uploadNewDocument(doc);
+ });
}
// (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
break;
@@ -392,7 +234,6 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
/** <MathJaxContext config={this.mathJaxConfig}> **/
<div className="chatBox">
{this.isInitializing && <div className="initializing-overlay">Initializing...</div>}
- {this.renderModal()}
<div
className="scroll-box chat-content"
ref={r => {
@@ -401,32 +242,37 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
}}>
<div className="messages">
- {this.history.map((message, index) => (
- <MessageComponent
- key={index}
- message={message}
- toggleToolLogs={this.toggleToolLogs}
- expandedLogIndex={this.expandedLogIndex}
- index={index}
- showModal={this.showModal}
- goToLinkedDoc={() => {}}
- setCurrentFile={this.setCurrentFile}
- onFollowUpClick={this.handleFollowUpClick}
- />
- ))}
- {!this.current_message ? null : (
- <MessageComponent
- key={this.history.length}
- message={this.current_message}
- toggleToolLogs={this.toggleToolLogs}
- expandedLogIndex={this.expandedLogIndex}
- index={this.history.length}
- showModal={this.showModal}
- goToLinkedDoc={() => {}}
- setCurrentFile={this.setCurrentFile}
- onFollowUpClick={this.handleFollowUpClick}
- />
- )}
+ {
+ //this.history.map((message, index) => (
+ // <MessageComponent
+ // key={index}
+ // message={message}
+ // toggleToolLogs={this.toggleToolLogs}
+ // expandedLogIndex={this.expandedLogIndex}
+ // index={index}
+ // showModal={this.showModal}
+ // goToLinkedDoc={() => {}}
+ // setCurrentFile={this.setCurrentFile}
+ // onFollowUpClick={this.handleFollowUpClick}
+ // />
+ //)
+ //)
+ }
+ {
+ //!this.current_message ? null : (
+ // <MessageComponent
+ // key={this.history.length}
+ // message={this.current_message}
+ // toggleToolLogs={this.toggleToolLogs}
+ // expandedLogIndex={this.expandedLogIndex}
+ // index={this.history.length}
+ // showModal={this.showModal}
+ // goToLinkedDoc={() => {}}
+ // setCurrentFile={this.setCurrentFile}
+ // onFollowUpClick={this.handleFollowUpClick}
+ // />
+ //)
+ }
</div>
</div>
<form onSubmit={this.askGPT} className="chat-form">