From c789d3d41a68c89e75fdfc12b1b05377ceef32d1 Mon Sep 17 00:00:00 2001
From: "A.J. Shulman" <Shulman.aj@gmail.com>
Date: Tue, 9 Jul 2024 13:55:03 -0400
Subject: starting to improve vectorstore

---
 src/client/views/nodes/ChatBox/ChatBox.tsx | 304 +++++++----------------------
 1 file changed, 75 insertions(+), 229 deletions(-)

(limited to 'src/client/views/nodes/ChatBox/ChatBox.tsx')

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,
@@ -117,50 +108,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();
@@ -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">
-- 
cgit v1.2.3-70-g09d2

