aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ChatBox/ChatBox.tsx
diff options
context:
space:
mode:
authorA.J. Shulman <Shulman.aj@gmail.com>2024-06-27 16:36:40 -0400
committerA.J. Shulman <Shulman.aj@gmail.com>2024-06-27 16:36:40 -0400
commite297c75cdcc8bb5b1b138d1272f1f6f27b222f4c (patch)
tree2b4c955188a4867d9c018ca0f9d7d4b099f6086d /src/client/views/nodes/ChatBox/ChatBox.tsx
parent33621442bad6ffe78840dc95984199d3b339d832 (diff)
fixing to work with python API
also added follow up questions
Diffstat (limited to 'src/client/views/nodes/ChatBox/ChatBox.tsx')
-rw-r--r--src/client/views/nodes/ChatBox/ChatBox.tsx351
1 files changed, 92 insertions, 259 deletions
diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx
index 390f13ce7..b986c7393 100644
--- a/src/client/views/nodes/ChatBox/ChatBox.tsx
+++ b/src/client/views/nodes/ChatBox/ChatBox.tsx
@@ -21,6 +21,7 @@ import './ChatBox.scss';
import MessageComponent from './MessageComponent';
import { ANNOTATION_LINK_TYPE, ASSISTANT_ROLE, AssistantMessage, DOWNLOAD_TYPE } from './types';
import { Annotation } from 'mobx/dist/internal';
+import { FormEvent } from 'react';
@observer
export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -33,7 +34,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable isInitializing: boolean = true;
@observable expandedLogIndex: number | null = null;
@observable linked_docs_to_add: Doc[] = [];
-
+ @observable inputValue: string = '';
private openai: OpenAI;
private assistantID: string = '';
private threadID: string = '';
@@ -161,239 +162,65 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
- runAssistant = async (inputText: string) => {
- // Ensure an assistant and thread are created
- if (!this.assistantID || !this.threadID || !this.vectorStoreID) {
- await this.createAssistant();
- console.log('Assistant and thread created:', this.assistantID, this.threadID);
- }
-
- let currentText: string = '';
- let currentToolCallMessage: string = '';
-
- // Send the user's input to the assistant
- await this.openai.beta.threads.messages.create(this.threadID, {
- role: 'user',
- content: inputText,
- });
-
- // Listen to the streaming responses
- const stream = this.openai.beta.threads.runs
- .stream(this.threadID, {
- assistant_id: this.assistantID,
- })
- .on('runStepCreated', (runStep: RunStep) => {
- runInAction(() => {
- this.isLoading = true;
- //intentionally don't merge run steps' messages and keep them as seperate messages on the interface
- this.current_message = { role: ASSISTANT_ROLE.ASSISTANT, text: '', tool_logs: '' };
- });
- })
- .on('toolCallDelta', (toolCallDelta, snapshot) => {
- if (toolCallDelta.type === 'code_interpreter') {
- if (toolCallDelta.code_interpreter?.input) {
- currentToolCallMessage += toolCallDelta.code_interpreter.input;
-
- if (this.current_message) {
- this.current_message.tool_logs = currentToolCallMessage;
- }
- }
-
- if (toolCallDelta.code_interpreter?.outputs) {
- currentToolCallMessage += '\n Code interpreter output:';
- toolCallDelta.code_interpreter.outputs.forEach(output => {
- if (output.type === 'logs') {
- runInAction(() => {
- if (this.current_message) {
- this.current_message.tool_logs += '\n|' + output.logs;
- }
- });
- }
- });
- }
- }
- })
- .on('textDelta', (textDelta, snapshot) => {
- currentText += textDelta.value;
- runInAction(() => {
- if (this.current_message) {
- this.current_message.text = currentText;
- }
- });
- })
- .on('messageDone', async message => {
- console.log(this.current_message);
- const textItem = message.content.find(item => item.type === 'text');
- if (textItem && textItem.type === 'text') {
- const { text } = textItem;
- console.log(text.value);
- try {
- if (this.current_message) {
- this.current_message.text = text.value;
- }
- } catch (e) {
- console.error('Error parsing JSON response:', e);
- }
- console.log(this.current_message);
-
- const { annotations } = text;
- console.log('Annotations: ' + annotations);
-
- await this.createLinks(
- annotations.filter(
- (annotation => {
- const seenAnnotationTexts = new Set<string>();
- return annotation => {
- if (seenAnnotationTexts.has(annotation.text)) {
- return false;
- } else {
- seenAnnotationTexts.add(annotation.text);
- return true;
- }
- };
- })()
- )
- );
- }
- runInAction(() => {
- if (this.current_message) {
- console.log(this.current_message);
- this.history.push({ ...this.current_message });
- this.current_message = undefined;
- }
- });
- })
- .on('toolCallDone', async toolCall => {
- runInAction(() => {
- if (this.current_message?.tool_logs) {
- this.history.push({ ...this.current_message });
- this.current_message = undefined;
- }
- });
- })
- .on('imageFileDone', (content: ImageFile, snapshot: Message) => {
- console.log('Image file done:', content);
- });
- };
-
- createLinks = async (annotations: OpenAI.Beta.Threads.Messages.Annotation[]) => {
- console.log(this.current_message);
- let text = this.current_message?.text;
- console.log(text);
- await Promise.all(
- annotations.map(async annotation => {
- const subString = annotation.text;
- const textToDisplay = `DASHLINK`;
- let fileInfo = '';
- let formattedLink = '';
- const fileName = subString.split('/')[subString.split('/').length - 1];
-
- if (annotation.type === 'file_path') {
- const { file_path: filePath } = annotation;
- if (filePath) {
- fileInfo = filePath.file_id + '!!!' + fileName;
- formattedLink = `[${textToDisplay}](${fileInfo}~~~${ANNOTATION_LINK_TYPE.DOWNLOAD_FILE})`;
- }
- } else {
- const { file_citation: fileCitation } = annotation;
- if (fileCitation) {
- const citedFile = await this.openai.files.retrieve(fileCitation.file_id);
- formattedLink = `[${textToDisplay}](${citedFile.filename}~~~${ANNOTATION_LINK_TYPE.DASH_DOC})`;
- }
- }
-
- console.log(formattedLink);
- text = text?.split(subString).join(formattedLink);
- console.log(text);
- })
- );
- runInAction(() => {
- if (this.current_message) this.current_message.text = text || '';
- });
- };
-
- @action
- goToLinkedDoc = async (link: string) => {
- const linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.Document)
- .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
- .map(d => DocCast(d?.annotationOn, d))
- .filter(d => d);
-
- const linkedDoc = linkedDocs.find(doc => {
- const docUrl = CsvCast(doc.data, PDFCast(doc.data)).url.pathname.replace('/files/pdfs/', '').replace('/files/csvs/', '');
- console.log('URL: ' + docUrl + ' Citation URL: ' + link);
- return link === docUrl;
- });
-
- if (linkedDoc) {
- await DocumentManager.Instance.showDocument(DocCast(linkedDoc), { willZoomCentered: true }, () => {});
- }
- };
-
- @action
askGPT = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
+ this.inputValue = '';
const textInput = event.currentTarget.elements.namedItem('messageInput') as HTMLInputElement;
const trimmedText = textInput.value.trim();
- if (!this.assistantID || !this.threadID) {
- try {
- await this.createAssistant();
- } catch (err) {
- console.error('Error:', err);
- }
- }
-
if (trimmedText) {
try {
textInput.value = '';
runInAction(() => {
this.history.push({ role: ASSISTANT_ROLE.USER, text: trimmedText });
});
- await this.runAssistant(trimmedText);
- this.dataDoc.data = this.history.toString();
+ const { response } = await Networking.PostToServer('/askAgent', { input: trimmedText });
+ runInAction(() => {
+ this.history.push({ role: ASSISTANT_ROLE.ASSISTANT, text: response });
+ });
+ this.dataDoc.data = JSON.stringify(this.history);
} catch (err) {
console.error('Error:', err);
}
}
};
- @action
- uploadLinks = async (linkedDocs: Doc[]) => {
- if (this.isInitializing) {
- console.log('Initialization in progress, upload aborted.');
- return;
- }
- const urls = linkedDocs.map(doc => CsvCast(doc.data, PDFCast(doc.data)).url.pathname);
- const csvUrls = urls.filter(url => url.endsWith('.csv'));
- console.log(this.assistantID, this.threadID, urls);
-
- const { openai_file_ids: openaiFileIds } = await Networking.PostToServer('/uploadPDFToVectorStore', { urls, threadID: this.threadID, assistantID: this.assistantID, vector_store_id: this.vectorStoreID });
-
- linkedDocs.forEach((doc, i) => {
- doc[this.Document[Id] + '_ai_field_id'] = openaiFileIds[i];
- console.log('AI Field ID: ' + openaiFileIds[i]);
- });
-
- if (csvUrls.length > 0) {
- for (let i = 0; i < csvUrls.length; i++) {
- this.linkedCsvIDs.push(openaiFileIds[urls.indexOf(csvUrls[i])]);
- }
- console.log('linked csvs:' + this.linkedCsvIDs);
- await this.openai.beta.assistants.update(this.assistantID, {
- tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
- tool_resources: {
- file_search: {
- vector_store_ids: [this.vectorStoreID],
- },
- code_interpreter: {
- file_ids: this.linkedCsvIDs,
- },
- },
- });
- }
- };
+ // @action
+ // uploadLinks = async (linkedDocs: Doc[]) => {
+ // if (this.isInitializing) {
+ // console.log('Initialization in progress, upload aborted.');
+ // return;
+ // }
+ // const urls: string[] = linkedDocs.map(doc => CsvCast(doc.data, PDFCast(doc.data)).url.pathname);
+ // const csvUrls: string[] = urls.filter(url => url.endsWith('.csv'));
+ // console.log(this.assistantID, this.threadID, urls);
+
+ // await Networking.PostToServer('/uploadPDFs', { file_path: urls[0] });
+
+ // // linkedDocs.forEach((doc, i) => {
+ // // doc[this.Document[Id] + '_ai_field_id'] = openaiFileIds[i];
+ // // console.log('AI Field ID: ' + openaiFileIds[i]);
+ // // });
+
+ // // if (csvUrls.length > 0) {
+ // // for (let i = 0; i < csvUrls.length; i++) {
+ // // this.linkedCsvIDs.push(openaiFileIds[urls.indexOf(csvUrls[i])]);
+ // // }
+ // // console.log('linked csvs:' + this.linkedCsvIDs);
+ // // await this.openai.beta.assistants.update(this.assistantID, {
+ // // tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
+ // // tool_resources: {
+ // // file_search: {
+ // // vector_store_ids: [this.vectorStoreID],
+ // // },
+ // // code_interpreter: {
+ // // file_ids: this.linkedCsvIDs,
+ // // },
+ // // },
+ // // });
+ // // }
+ // };
downloadToComputer = (url: string, fileName: string) => {
fetch(url, { method: 'get', mode: 'no-cors', referrerPolicy: 'no-referrer' })
@@ -555,53 +382,59 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
}
+ @action
+ handleFollowUpClick = (question: string) => {
+ console.log('Follow-up question clicked:', question);
+ this.inputValue = question;
+ };
render() {
return (
- <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 => {
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = r;
- 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={this.goToLinkedDoc}
- setCurrentFile={this.setCurrentFile}
- />
- ))}
- {!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={this.goToLinkedDoc}
- setCurrentFile={this.setCurrentFile}
- isCurrent
- />
- )}
- </div>
+ /** <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 => {
+ this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+ this._oldWheel = r;
+ 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}
+ />
+ )}
</div>
- <form onSubmit={this.askGPT} className="chat-form">
- <input type="text" name="messageInput" autoComplete="off" placeholder="Type a message..." />
- <button type="submit">Send</button>
- </form>
</div>
- </MathJaxContext>
+ <form onSubmit={this.askGPT} className="chat-form">
+ <input type="text" name="messageInput" autoComplete="off" placeholder="Type a message..." value={this.inputValue} onChange={e => (this.inputValue = e.target.value)} />
+ <button type="submit">Send</button>
+ </form>
+ </div>
+ /** </MathJaxContext> **/
);
}
}