aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts
blob: 446fe34422f78a3000c40ae7305429acbd0e28fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { action } from "mobx";
import { Upload } from "openai/resources";
import { ClientUtils } from "../../../../../../ClientUtils";
import { Networking } from "../../../../../Network";
import { gptImageCall, gptAPICall, GPTCallType } from "../../../../../apis/gpt/GPT";
import { Col } from "../DocCreatorMenu";
import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend";
import { TemplateField, ViewType } from "../TemplateFieldTypes/TemplateField";
import { Template } from "../Template";
import { Doc } from "../../../../../../fields/Doc";
import { DrawingFillHandler } from "../../../../smartdraw/DrawingFillHandler";
import { CollectionFreeFormView } from "../../../../collections/collectionFreeForm";

export class TemplateMenuAIUtils {

    public static generateGPTImage = async (prompt: string): Promise<string | undefined> => {
        try {
            const res = await gptImageCall(prompt);

            if (res) {
                const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: res })) as Upload.FileInformation[];
                const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client);
                return source;
            }
        } catch (e) {
            console.log(e);
        }
    };

    public static renderGPTImageCall = async (template: Template, col: Col, fieldNumber: number | undefined): Promise<boolean> => {
        const generateAndLoadImage = async (fieldNum: string, column: Col, prompt: string) => {
            const url = await this.generateGPTImage(prompt);
            const field: TemplateField = template.getFieldByID(Number(fieldNum));

            field.setContent(url ?? '', ViewType.IMG);
            field.setTitle(col.title);
        };

        const fieldContent: string = template.compiledContent;

        try {
            const sysPrompt =
                'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' +
                fieldContent +
                ' **** The user prompt is: ' +
                col.desc;

            const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT);

            await generateAndLoadImage(String(fieldNumber), col, prompt);
        } catch (e) {
            console.log(e);
        }
        return true;
    };

    public static renderGPTTextCall = async (template: Template, col: Col, fieldNum: number | undefined): 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} ---`;

        const fieldContent: string = template.compiledContent;

        try {
            const prompt = fieldContent + textAssignment;

            const res = await gptAPICall(`${Math.random() * 100000}: ${prompt}`, GPTCallType.FILL);

            if (res) {
                const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
                Object.entries(assignments).forEach(([, /* title */ info]) => {
                    const field: TemplateField = template.getFieldByID(Number(info.number));

                    field.setContent(info.content ?? '', ViewType.TEXT);
                    field.setTitle(col.title);
                });
            }
        } catch (err) {
            console.log(err);
        }

        return true;
    };

    /**
     * Populates a preset template framework with content from a datavizbox or any AI-generated content.
     * @param template the preloaded template framework being filled in
     * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz
     * @returns a doc containing the fully rendered template
     */
    public static applyGPTContentToTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Template | undefined> => {
        const GPTTextCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.TEXT && !col.AIGenerated);
        const GPTIMGCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.VISUAL && !col.AIGenerated);

        if (GPTTextCalls.length) {
            const promises = GPTTextCalls.map(([str, col]) => {
                return TemplateMenuAIUtils.renderGPTTextCall(template, col, Number(str));
            });

            await Promise.all(promises);
        }

        if (GPTIMGCalls.length) {
            const promises = GPTIMGCalls.map(async ([fieldNum, col]) => {
                return TemplateMenuAIUtils.renderGPTImageCall(template, col, Number(fieldNum));
            });

            await Promise.all(promises);
        }

        return template;
    };

}