aboutsummaryrefslogtreecommitdiff
path: root/src/client/apis/gpt/GPT.ts
blob: 5be9d84ffaf114ac03a97b70faf20e3b63a9614f (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import { ChatCompletionMessageParam, Image } from 'openai/resources';
import { openai } from './setup';

enum GPTCallType {
    SUMMARY = 'summary',
    COMPLETION = 'completion',
    EDIT = 'edit',
    CHATCARD = 'chatcard',
    FLASHCARD = 'flashcard',
    QUIZ = 'quiz',
    SORT = 'sort',
    DESCRIBE = 'describe',
    MERMAID = 'mermaid',
    DATA = 'data',
    RUBRIC = 'rubric',
    TYPE = 'type',
    SUBSET = 'subset',
    INFO = 'info',
}

type GPTCallOpts = {
    model: string;
    maxTokens: number;
    temp: number;
    prompt: string;
};

const callTypeMap: { [type: string]: GPTCallOpts } = {
    // newest model: gpt-4
    summary: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize the text given in simpler terms.' },
    edit: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword the text.' },
    flashcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with each question and answer labeled. Do not label each flashcard and do not include asterisks: ' },

    completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." },
    mermaid: {
        model: 'gpt-4-turbo',
        maxTokens: 2048,
        temp: 0,
        prompt: "(Heres an example of changing color of a pie chart to help you pie title Example \"Red\": 20 \"Blue\": 50 \"Green\": 30 %%{init: {'theme': 'base', 'themeVariables': {'pie1': '#0000FF', 'pie2': '#00FF00', 'pie3': '#FF0000'}}}%% keep in mind that pie1 is the highest since its sorted in descending order. Heres an example of a mindmap: mindmap  root((mindmap))    Origins      Long history      ::icon(fa fa-book)      Popularisation        British popular psychology author Tony Buzan    Research      On effectivness<br/>and features      On Automatic creation       Uses            Creative techniques            Strategic planning            Argument mapping    Tools      Pen and paper     Mermaid.  ",
    },
    data: {
        model: 'gpt-3.5-turbo',
        maxTokens: 256,
        temp: 0.5,
        prompt: "You are a helpful resarch assistant. Analyze the user's data to find meaningful patterns and/or correlation. Please only return a JSON with a correlation column 1 propert, a correlation column 2 property, and an analysis property. ",
    },
    sort: {
        model: 'gpt-4o',
        maxTokens: 2048,
        temp: 0.25,
        prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas",
    },
    describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
    chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
    quiz: {
        model: 'gpt-4-turbo',
        maxTokens: 1024,
        temp: 0,
        prompt: "BRIEFLY (<50 words) describe any differences between the rubric and the user's answer answer in second person. If there are no differences, say correct",
    },

    rubric: {
        model: 'gpt-4-turbo',
        maxTokens: 1024,
        temp: 0,
        prompt: "BRIEFLY (<25 words) provide a definition for the following term. It will be used as a rubric to evaluate the user's understanding of the topic",
    },

    type: {
        model: 'gpt-4-turbo',
        maxTokens: 1024,
        temp: 0,
        prompt: "I'm going to provide you with a question. Based on the question, is the user asking you to 1. Assigns docs with tags(like star / heart etc)/labels, 2. Filter docs, 3. Provide information about a specific doc 4. Provide a specific doc based on a question/information 5. Provide general information 6. Put cards in a specific order. Answer with only the number for 2-6. For number one, provide the number (1) and the appropriate tag",
    },

    subset: {
        model: 'gpt-4-turbo',
        maxTokens: 1024,
        temp: 0,
        prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas",
    },

    info: {
        model: 'gpt-4-turbo',
        maxTokens: 1024,
        temp: 0,
        prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)",
    },
};

let lastCall = '';
let lastResp = '';
/**
 * Calls the OpenAI API.
 *
 * @param inputText Text to process
 * @returns AI Output
 */
const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: string) => {
    const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ].includes(callType) ? inputTextIn + '.' : inputTextIn;
    const opts: GPTCallOpts = callTypeMap[callType];
    if (lastCall === inputText) return lastResp;
    try {
        lastCall = inputText;

        const usePrompt = prompt ? prompt + opts.prompt : opts.prompt;
        const messages: ChatCompletionMessageParam[] = [
            { role: 'system', content: usePrompt },
            { role: 'user', content: inputText },
        ];

        const response = await openai.chat.completions.create({
            model: opts.model,
            messages: messages,
            temperature: opts.temp,
            max_tokens: opts.maxTokens,
        });
        lastResp = response.choices[0].message.content ?? '';
        return lastResp;
    } catch (err) {
        console.log(err);
        return 'Error connecting with API.';
    }
};
const gptImageCall = async (prompt: string, n?: number) => {
    try {
        const response = await openai.images.generate({
            prompt: prompt,
            n: n ?? 1,
            size: '1024x1024',
        });
        return response.data.map((data: Image) => data.url);
        // return response.data.data[0].url;
    } catch (err) {
        console.error(err);
    }
    return undefined;
};
const gptGetEmbedding = async (src: string): Promise<number[]> => {
    try {
        const embeddingResponse = await openai.embeddings.create({
            model: 'text-embedding-3-large',
            input: [src],
            encoding_format: 'float',
            dimensions: 256,
        });

        // Assume the embeddingResponse structure is correct; adjust based on actual API response
        const { embedding } = embeddingResponse.data[0];
        return embedding;
    } catch (err) {
        console.log(err);
        return [];
    }
};
const gptImageLabel = async (src: string): Promise<string> => {
    try {
        const response = await openai.chat.completions.create({
            model: 'gpt-4o',
            messages: [
                {
                    role: 'user',
                    content: [
                        { type: 'text', text: 'Give three labels to describe this image.' },
                        {
                            type: 'image_url',
                            image_url: {
                                url: `${src}`,
                                detail: 'low',
                            },
                        },
                    ],
                },
            ],
        });
        if (response.choices[0].message.content) {
            return response.choices[0].message.content;
        }
        return 'Missing labels';
    } catch (err) {
        console.log(err);
        return 'Error connecting with API';
    }
};

export { gptAPICall, gptImageCall, GPTCallType, gptImageLabel, gptGetEmbedding };