aboutsummaryrefslogtreecommitdiff
path: root/src/client/apis/gpt
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/apis/gpt')
-rw-r--r--src/client/apis/gpt/GPT.ts115
-rw-r--r--src/client/apis/gpt/PresCustomization.ts133
-rw-r--r--src/client/apis/gpt/setup.ts8
3 files changed, 230 insertions, 26 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 8f58ec364..05007960d 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,10 +1,16 @@
-import { ClientOptions, OpenAI } from 'openai';
import { ChatCompletionMessageParam } 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',
}
@@ -15,16 +21,38 @@ type GPTCallOpts = {
prompt: string;
};
-/**
- * Replace completions (deprecated) with chat
- */
-
const callTypeMap: { [type: string]: GPTCallOpts } = {
// newest model: gpt-4
- summary: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize the text given in simpler terms.' },
- edit: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword the text.' },
- completion: { model: 'gpt-3.5-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." },
- 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 keep your response short and to the point." },
+ 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.5,
+ prompt: "I'm going to give you a list of descriptions. Each one is seperated by ====== on either side. They will vary in length, so make sure to only seperate when you see ======. Sort them into lists by shared content. MAKE SURE EACH DESCRIPTOR IS IN ONLY ONE LIST. Generate only the list with each list seperated by ====== with the elements seperated by ~~~~~~. Try to do around 4 groups, but a little more or less is ok.",
+ },
+ 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: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct',
+ },
};
let lastCall = '';
@@ -36,16 +64,11 @@ let lastResp = '';
* @returns AI Output
*/
const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: any) => {
- const inputText = callType === GPTCallType.SUMMARY ? inputTextIn + '.' : inputTextIn;
+ const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ].includes(callType) ? inputTextIn + '.' : inputTextIn;
const opts: GPTCallOpts = callTypeMap[callType];
if (lastCall === inputText) return lastResp;
try {
- const configuration: ClientOptions = {
- apiKey: process.env.OPENAI_KEY,
- dangerouslyAllowBrowser: true,
- };
lastCall = inputText;
- const openai = new OpenAI(configuration);
const usePrompt = prompt ? opts.prompt + prompt : opts.prompt;
const messages: ChatCompletionMessageParam[] = [
@@ -55,9 +78,9 @@ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: a
const response = await openai.chat.completions.create({
model: opts.model,
- max_tokens: opts.maxTokens,
+ messages: messages,
temperature: opts.temp,
- messages,
+ max_tokens: opts.maxTokens,
});
lastResp = response.choices[0].message.content ?? '';
return lastResp;
@@ -66,25 +89,65 @@ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: a
return 'Error connecting with API.';
}
};
-
const gptImageCall = async (prompt: string, n?: number) => {
try {
- const configuration: ClientOptions = {
- apiKey: process.env.OPENAI_KEY,
- dangerouslyAllowBrowser: true,
- };
-
- const openai = new OpenAI(configuration);
const response = await openai.images.generate({
prompt: prompt,
n: n ?? 1,
size: '1024x1024',
});
- return response.data.map(data => data.url);
+ return response.data.map((data: any) => 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 to five 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 };
+export { gptAPICall, gptImageCall, GPTCallType, gptImageLabel, gptGetEmbedding };
diff --git a/src/client/apis/gpt/PresCustomization.ts b/src/client/apis/gpt/PresCustomization.ts
new file mode 100644
index 000000000..2262886a2
--- /dev/null
+++ b/src/client/apis/gpt/PresCustomization.ts
@@ -0,0 +1,133 @@
+import { openai } from './setup';
+
+export enum CustomizationType {
+ PRES_TRAIL_SLIDE = 'trails',
+}
+
+interface PromptInfo {
+ description: string;
+ features: { name: string; description: string; values?: string[] }[];
+}
+const prompts: { [key: string]: PromptInfo } = {
+ trails: {
+ description:
+ 'We are customizing the properties and transition of a slide in a presentation. You are given the current properties of the slide in a json with the fields [title, presentation_transition, presentation_effect, config_zoom, presentation_effectDirection], as well as the prompt for how the user wants to change it. Return a json with the required fields: [title, presentation_transition, presentation_effect, config_zoom, presentation_effectDirection] by applying the changes in the prompt to the current state of the slide.',
+ features: [],
+ },
+};
+
+// Allows you to register properties that are customizable
+export const addCustomizationProperty = (type: CustomizationType, name: string, description: string, values?: string[]) => {
+ values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
+};
+
+// All the registered fields, make sure to update during registration, this
+// includes most fields but is not yet fully comprehensive
+export const gptSlideProperties = [
+ 'title',
+ 'config_zoom',
+ 'presentation_transition',
+ 'presentation_easeFunc',
+ 'presentation_effect',
+ 'presentation_effectDirection',
+ 'presentation_effectTiming',
+ 'presentation_playAudio',
+ 'presentation_zoomText',
+ 'presentation_hideBefore',
+ 'presentation_hide',
+ 'presentation_hideAfter',
+ 'presentation_openInLightbox',
+];
+
+// Registers slide properties
+const setupPresSlideCustomization = () => {
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'title', 'is the title/name of the slide.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_transition', 'is a number in milliseconds for how long it should take to transition/move to a slide.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_easeFunc', 'is the easing function for the movement to the slide.', ['Ease', 'Ease In', 'Ease Out', 'Ease Out', 'Ease In Out', 'Linear']);
+
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_effect', 'is an effect applied to the slide when we transition to it.', ['None', 'Expand', 'Fade in', 'Bounce', 'Flip', 'Rotate', 'Roll']);
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_effectDirection', 'is what direction the effect is applied.', ['Enter from left', 'Enter from right', 'Enter from bottom', 'Enter from Top', 'Enter from center']);
+ addCustomizationProperty(
+ CustomizationType.PRES_TRAIL_SLIDE,
+ 'presentation_effectTiming',
+ "is a json object of the format: {type: string, stiffness: number, damping: number, mass: number}. Type is always “custom”. Controls the spring-based timing of the presentation effect animation. Stiffness, damping, and mass control the physics-based properties of spring animations. This is used to create a more natural looking timing, bouncy effects, etc. Use spring physics to adjust these parameters to match the user's description of how they want to animate the effect."
+ );
+
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'config_zoom', 'is a number from 0 to 1.0 indicating the percentage we should zoom into the slide.');
+
+ // boolean values
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_playAudio', 'is a boolean value indicating if we should play audio when we go to the slide.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_zoomText', 'is a boolean value indicating if we should zoom into text selections when we go to the slide.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_hideBefore', 'is a boolean value indicating if we should hide the slide before going to it.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_hide', 'is a boolean value indicating if we should hide the slide during the presentation.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_hideAfter', 'is a boolean value indicating if we should hide the slide after going to it.');
+ addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_openInLightbox', 'is a boolean value indicating if we should open the slide in an overlay or lightbox view during the presentation.');
+};
+
+setupPresSlideCustomization();
+
+export const getSlideTransitionSuggestions = async (inputText: string) => {
+ /**
+ * Prompt: Generate an entrance animations from slower and gentler
+ * to bouncier and more high energy
+ *
+ * Format:
+ * {
+ * name: Slow Fade, Quick Flip, Springy
+ * effect: BOUNCE
+ * effectDirection: LEFT
+ * timingConfig: {
+ * }
+ * }
+ */
+
+ const prompt =
+ "I want to generate four distinct types of slide effect animations. Return a json of the form {effect: string, direction: string, stiffness: number, damping: number, mass: number}[] with four elements. Effect is the type of animation; its only possible values are ['Expand', 'Fade in', 'Bounce', 'Flip', 'Rotate', 'Roll']. Direction is the direction that the animation starts from; its only possible values are ['Enter from left', 'Enter from right', 'Enter from bottom', 'Enter from Top', 'Enter from center']. Stiffness, damping, and mass control the physics-based properties of spring animations. This is used to create a more natural-looking timing, bouncy effects, etc. Use spring physics to adjust these parameters to animate the effect.";
+
+ const customInput = inputText ?? 'Make them as contrasting as possible with different effects and timings ranging from gentle to energetic.';
+
+ try {
+ const response = await openai.chat.completions.create({
+ model: 'gpt-4',
+ messages: [
+ { role: 'system', content: prompt },
+ { role: 'user', content: `${customInput}` },
+ ],
+ temperature: 0,
+ max_tokens: 1000,
+ });
+ return response.choices[0].message?.content;
+ } catch (err) {
+ console.log(err);
+ return 'Error connecting with API.';
+ }
+};
+
+export const gptTrailSlideCustomization = async (inputText: string, properties: any | any[]) => {
+ let prompt = prompts.trails.description;
+
+ prompts.trails.features.forEach(feature => {
+ prompt += feature.name + ' ' + feature.description;
+ if (feature.values) {
+ prompt += `Its only possible values are [${feature.values.join(', ')}].`;
+ }
+ });
+
+ prompt += 'Set unchanged values to null and make sure you include new properties if they are specified in the prompt even if they do not exist in current properties. Please only return the json with the keys described and their values.';
+
+ try {
+ const response = await openai.chat.completions.create({
+ model: 'gpt-4',
+ messages: [
+ { role: 'system', content: prompt },
+ { role: 'user', content: `Prompt: ${inputText}, Current properties: ${JSON.stringify(properties)}` },
+ ],
+ temperature: 0,
+ max_tokens: 1000,
+ });
+ return response.choices[0].message?.content;
+ } catch (err) {
+ console.log(err);
+ return 'Error connecting with API.';
+ }
+};
diff --git a/src/client/apis/gpt/setup.ts b/src/client/apis/gpt/setup.ts
new file mode 100644
index 000000000..f648542f2
--- /dev/null
+++ b/src/client/apis/gpt/setup.ts
@@ -0,0 +1,8 @@
+import { ClientOptions, OpenAI } from 'openai';
+
+const configuration: ClientOptions = {
+ apiKey: process.env.OPENAI_KEY,
+ dangerouslyAllowBrowser: true,
+};
+
+export const openai = new OpenAI(configuration);