From 9a188952fc50cb82ebaebf3b0e3bf5183946f5c9 Mon Sep 17 00:00:00 2001
From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com>
Date: Thu, 7 Nov 2024 05:20:00 -0500
Subject: GPT text generation working fully!!

---
 .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx   | 172 ++++++++++++---------
 1 file changed, 96 insertions(+), 76 deletions(-)

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

diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
index 379a33a99..ea38430ca 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
@@ -61,7 +61,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     @observable _selectedTemplate: Template | undefined = undefined;
     @observable _currEditingTemplate: Template | undefined = undefined;
 
-    @observable _userCreatedColumns: Col[] = [];
+    @observable _userCreatedFields: Col[] = [];
     @observable _selectedCols: { title: string; type: string; desc: string }[] | undefined = [];
 
     @observable _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 3, repeat: 0 };
@@ -175,7 +175,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
 
                 return col;
             })
-            .concat(this._userCreatedColumns);
+            .concat(this._userCreatedFields);
     }
 
     @computed get canMakeDocs() {
@@ -360,14 +360,14 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         return undefined;
     }
 
-    @action updateSelectedTemplate = (template: Template) => {
+    @action updateSelectedTemplate = async (template: Template) => {
         if (this._selectedTemplate === template) {
             this._selectedTemplate = undefined;
             return;
         } else {
             this._selectedTemplate = template;
             template.renderUpdates();
-            this._fullyRenderedDocs = this.createDocsFromTemplate(template) ?? [];
+            this._fullyRenderedDocs = await this.createDocsFromTemplate(template) ?? [];
             this.updateRenderedDocCollection();
         }
     };
@@ -410,15 +410,15 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     };
 
     @action addField = () => {
-        const newFields: Col[] = this._userCreatedColumns.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [] }]);
-        this._userCreatedColumns = newFields;
+        const newFields: Col[] = this._userCreatedFields.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [] }]);
+        this._userCreatedFields = newFields;
     };
 
     @action removeField = (field: { title: string; type: string; desc: string }) => {
         if (this._dataViz?.axes.includes(field.title)) {
             this._dataViz.selectAxes(this._dataViz.axes.filter(col => col !== field.title));
         } else {
-            const toRemove = this._userCreatedColumns.filter(f => f === field);
+            const toRemove = this._userCreatedFields.filter(f => f === field);
             if (!toRemove) return;
 
             if (toRemove.length > 1) {
@@ -427,10 +427,10 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                 }
             }
 
-            if (this._userCreatedColumns.length === 1) {
-                this._userCreatedColumns = [];
+            if (this._userCreatedFields.length === 1) {
+                this._userCreatedFields = [];
             } else {
-                this._userCreatedColumns.splice(this._userCreatedColumns.indexOf(toRemove[0]), 1);
+                this._userCreatedFields.splice(this._userCreatedFields.indexOf(toRemove[0]), 1);
             }
         }
     };
@@ -498,22 +498,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
      * @returns a doc containing the fully rendered template
      */
     applyGPTContentToTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Template | undefined> => {
-        const wordLimit = (size: TemplateFieldSize) => {
-            switch (size) {
-                case TemplateFieldSize.TINY:
-                    return 2;
-                case TemplateFieldSize.SMALL:
-                    return 5;
-                case TemplateFieldSize.MEDIUM:
-                    return 20;
-                case TemplateFieldSize.LARGE:
-                    return 50;
-                case TemplateFieldSize.HUGE:
-                    return 100;
-                default:
-                    return 10;
-            }
-        };
 
         const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => {
             const url = await this.generateGPTImage(prompt);
@@ -523,44 +507,17 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
             field.setTitle(col.title);
         };
 
-        const GPTTextCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.TEXT);
-        const GPTIMGCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.VISUAL);
-
-        if (!GPTTextCalls && !GPTIMGCalls) return;
-
-        const stringifyGPTInfo = (calls: [string, Col][]): string => {
-            let string: string = '*** COLUMN INFO:';
-            calls.forEach(([fieldNum, col]) => {
-                string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`;
-            });
-            return (string += ' ***');
-        };
-
-        const GPTTextAssignment = stringifyGPTInfo(GPTTextCalls);
-
+        const GPTTextCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.TEXT && this._userCreatedFields.includes(col));
+        const GPTIMGCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.VISUAL && this._userCreatedFields.includes(col));
 
         let fieldContent: string = template.compiledContent;
 
         if (GPTTextCalls.length) {
-            try {
-                const prompt = fieldContent + GPTTextAssignment;
-
-                const res = await gptAPICall(`${++this._callCount}: ${prompt}`, GPTCallType.FILL);
-
-                if (res) {
-                    const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
-                    //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments);
-                    Object.entries(assignments).forEach(([title, info]) => {
-                        const field: Field = template.getFieldByID(Number(info.number));
-                        const col = this.getColByTitle(title);
+            const promises = GPTTextCalls.map(([str, col]) => {
+                return this.renderGPTTextCall(template, col, Number(str));
+            });
 
-                        field.setContent(info.content ?? '', FieldContentType.STRING);
-                        field.setTitle(col.title);
-                    });
-                }
-            } catch (err) {
-                console.log(err);
-            }
+            await Promise.all(promises);
         }
 
         if (GPTIMGCalls.length) {
@@ -634,7 +591,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     const toObj = Object.entries(assignment).reduce(
                         (a, [fieldID, colTitle]) => {
                             const col = this.getColByTitle(colTitle);
-                            if (!this._userCreatedColumns.includes(col)){ // do the following for any fields not added by the user; will change in the future, for now only GPT content works with user-added fields
+                            if (!this._userCreatedFields.includes(col)){ // do the following for any fields not added by the user; will change in the future, for now only GPT content works with user-added fields
                                 const field = template.getFieldByID(Number(fieldID));
                                 //console.log(field);
                                 field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? FieldContentType.IMAGE : FieldContentType.STRING);
@@ -676,38 +633,101 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         });
     };
 
-    createDocsFromTemplate = (template: Template) => {
+    renderGPTTextCall = async (template: Template, col: Col, fieldNum: number): Promise<boolean> => {
+        const wordLimit = (size: TemplateFieldSize) => {
+            switch (size) {
+                case TemplateFieldSize.TINY:
+                    return 2;
+                case TemplateFieldSize.SMALL:
+                    return 5;
+                case TemplateFieldSize.MEDIUM:
+                    return 20;
+                case TemplateFieldSize.LARGE:
+                    return 50;
+                case TemplateFieldSize.HUGE:
+                    return 100;
+                default:
+                    return 10;
+            }
+        };
+
+        const textAssignment = `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`;
+
+        let fieldContent: string = template.compiledContent;
+
+        try {
+            const prompt = fieldContent + textAssignment;
+
+            const res = await gptAPICall(`${++this._callCount}: ${prompt}`, GPTCallType.FILL);
+
+            console.log('prompt: ', prompt, ' response: ', res);
+
+            if (res) {
+                const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
+                Object.entries(assignments).forEach(([title, info]) => {
+                    const field: Field = template.getFieldByID(Number(info.number));
+                    const col = this.getColByTitle(title);
+
+                    field.setContent(info.content ?? '', FieldContentType.STRING);
+                    field.setTitle(col.title);
+                });
+            }
+        } catch (err) {
+            console.log(err);
+        }
+
+        return true;
+    }
+
+    createDocsFromTemplate = async (template: Template) => {
         const dv = this._dataViz;
         if (!dv) return;
         const fields: string[] = Array.from(Object.keys(dv.records[0]));  
         const selectedRows = NumListCast(dv.layoutDoc.dataViz_selectedRows);
-
-        const fieldContents: {[title: string]: string}[] = selectedRows.map(row => {
-            let values: {[title: string]: string} = {};
+    
+        const rowContents: { [title: string]: string }[] = selectedRows.map(row => {
+            let values: { [title: string]: string } = {};
             fields.forEach(col => {
                 values[col] = dv.records[row][col];
             });
-
+    
             return values;
         });
+    
+        const processContent = async (content: {[title: string]: string}) => {
+            const templateCopy = template.cloneBase(); 
+    
+            console.log('rows: ', content, '---------------- fields: ', fields)
 
-
-        const renderedDocs: Doc[] = [];
-
-        fieldContents.forEach(content => {
             fields.filter(title => title).forEach(title => {
-                const field = template.getFieldByTitle(title);
-                if (field === undefined){
-                    return;
-                }
+                const field = templateCopy.getFieldByTitle(title);
+                console.log('field: ', field);
+                if (field === undefined) { return };
                 field.setContent(content[title]);
             });
-
-            renderedDocs.push(template.mainField.renderedDoc());
-        });
+    
+            const gptPromises = this._userCreatedFields.map(field => {
+                const title = field.title;
+                const templateField = templateCopy.getFieldByTitle(title);
+                if (templateField === undefined) { return };
+                const templatefieldID = templateField.getID;
+    
+                return this.renderGPTTextCall(templateCopy, field, templatefieldID);
+            });
+    
+            await Promise.all(gptPromises);
+    
+            console.log(templateCopy);
+            return templateCopy.mainField.renderedDoc();
+        };
+    
+        const promises = rowContents.map(content => processContent(content));
+    
+        const renderedDocs = await Promise.all(promises);
 
         return renderedDocs;
     }
+    
 
     addRenderedCollectionToMainview = () => {
         const collection = this._renderedDocCollection;
-- 
cgit v1.2.3-70-g09d2

