From ed94cc8fb516455dbeea223d0fab25e4fb1c1d78 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Mon, 27 Feb 2023 16:56:02 -0500 Subject: feat: added summarization functionality to WebBox and cleaned up code --- src/client/apis/gpt/Summarization.ts | 20 +++- src/client/views/MainView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 1 + src/client/views/nodes/WebBox.tsx | 6 ++ .../views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/client/views/pdf/AnchorMenu.tsx | 39 ++++--- src/client/views/pdf/GPTPopup.scss | 91 ++++++++++++++-- src/client/views/pdf/GPTPopup.tsx | 115 +++++++++++++++++++-- src/client/views/pdf/PDFViewer.tsx | 19 ++-- 9 files changed, 243 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/client/apis/gpt/Summarization.ts b/src/client/apis/gpt/Summarization.ts index 931e0e48f..c0dcc0267 100644 --- a/src/client/apis/gpt/Summarization.ts +++ b/src/client/apis/gpt/Summarization.ts @@ -1,22 +1,32 @@ import { Configuration, OpenAIApi } from 'openai'; +/** + * Summarizes the inputted text with OpenAI. + * + * @param text Text to summarize + * @returns Summary of text + */ const gptSummarize = async (text: string) => { text += '.'; + const model = 'text-curie-001'; // Most advanced: text-davinci-003 + const maxTokens = 200; + const temp = 0.8; + try { const configuration = new Configuration({ apiKey: process.env.OPENAI_KEY, }); const openai = new OpenAIApi(configuration); const response = await openai.createCompletion({ - model: 'text-curie-001', - max_tokens: 256, - temperature: 0.7, - prompt: `Summarize this text in one sentence: ${text}`, + model: model, + max_tokens: maxTokens, + temperature: temp, + prompt: `Summarize this text: ${text}`, }); return response.data.choices[0].text; } catch (err) { console.log(err); - return ''; + return 'Error connecting with API.'; } }; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index da64e7c12..d7603cf5a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -245,6 +245,7 @@ export class MainView extends React.Component { library.add( ...[ + fa.faExclamationCircle, fa.faEdit, fa.faTrash, fa.faTrashAlt, diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b88ac113e..40c04c08f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -485,6 +485,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined); AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale); + // Changing which document to add the annotation to (the currently selected WebBox) + GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}-${this._urlHash}-sidebar`); + GPTPopup.Instance.addDoc = this.sidebarAddDocument; } } }; @@ -780,6 +785,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { + console.log(annotationKey); (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.webUrl = this._url)); return this.addDocument(doc, annotationKey); }; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 683ccb9c4..b9327db0d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -888,7 +888,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (this._editorView && this._recording) { this.stopDictation(true); diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 6bcfbe4c2..8f9261614 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -45,7 +45,7 @@ export class AnchorMenu extends AntimodeMenu { @observable public Highlighting: boolean = false; @observable public Status: 'marquee' | 'annotation' | '' = ''; - // GPT additions (flow 2) + // GPT additions @observable private summarizedText: string = ''; @observable private loadingSummary: boolean = false; @observable private showGPTPopup: boolean = false; @@ -63,7 +63,7 @@ export class AnchorMenu extends AntimodeMenu { }; private selectedText: string = ''; - setSelectedText = (txt: string) => { + public setSelectedText = (txt: string) => { this.selectedText = txt; }; @@ -71,9 +71,6 @@ export class AnchorMenu extends AntimodeMenu { public OnCrop: (e: PointerEvent) => void = unimplementedFunction; public OnClick: (e: PointerEvent) => void = unimplementedFunction; - public OnSummary: (e: PointerEvent) => Promise = () => { - return new Promise(() => {}); - }; public OnAudio: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; @@ -123,14 +120,27 @@ export class AnchorMenu extends AntimodeMenu { ); } - getGPTSummary = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, returnFalse, e => this.OnSummary?.(e)); + /** + * Returns a mock summary simulating an API call. + * @returns A Promise that resolves into a string + */ + mockSummarize = async (): Promise => { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve('Mock summary. This is a summary of the highlighted text.'); + }, 1000); + }); }; + /** + * Invokes the API with the selected text and stores it in the summarized text. + * @param e pointer down event + */ invokeGPT = async (e: React.PointerEvent) => { this.setGPTPopupVis(true); this.setLoading(true); const res = await gptSummarize(this.selectedText); + // const res = await this.mockSummarize(); if (res) { this.setSummarizedText(res); } else { @@ -243,12 +253,15 @@ export class AnchorMenu extends AntimodeMenu { - Summarize with AI}> - - - + {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/} + {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && ( + Summarize with AI}> + + + )} + {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( Click to Record Annotation}> + + + ) : ( +
+ + + +
+ )} + + )} + {this.summaryDone && ( +
+ + AI generated responses can contain inaccurate or misleading content.{' '} + + Learn More + +
)} ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 324f31f23..408ba07d1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -25,6 +25,7 @@ import { Annotation } from './Annotation'; import './PDFViewer.scss'; import React = require('react'); import { gptSummarize } from '../../apis/gpt/Summarization'; +import { GPTPopup } from './GPTPopup'; const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer'); const pdfjsLib = require('pdfjs-dist'); const _global = (window /* browser */ || global) /* node */ as any; @@ -41,6 +42,7 @@ interface IViewerProps extends FieldViewProps { fieldKey: string; pdf: Pdfjs.PDFDocumentProxy; url: string; + sidebarAddDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean; loaded?: (nw: number, nh: number, np: number) => void; setPdfViewer: (view: PDFViewer) => void; anchorMenuClick?: () => undefined | ((anchor: Doc, summarize?: boolean) => void); @@ -83,19 +85,6 @@ export class PDFViewer extends React.Component { return AnchorMenu.Instance?.GetAnchor; } - // Fields for using GPT to summarize selected text - private _summaryText: string = ''; - setSummaryText = async () => { - try { - const summary = await gptSummarize(this.selectionText()); - this._summaryText = `Summary: ${summary}`; - } catch (err) { - console.log(err); - this._summaryText = 'Failed to fetch summary.'; - } - }; - summaryText = () => this._summaryText; - selectionText = () => this._selectionText; selectionContent = () => this._selectionContent; @@ -435,6 +424,10 @@ export class PDFViewer extends React.Component { this.createTextAnnotation(sel, sel.getRangeAt(0)); AnchorMenu.Instance.jumpTo(e.clientX, e.clientY); } + + // Changing which document to add the annotation to (the currently selected PDF) + GPTPopup.Instance.setSidebarId('data-sidebar'); + GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; }; @action -- cgit v1.2.3-70-g09d2