From d78fc09435169b61362257e22e668a51f808da64 Mon Sep 17 00:00:00 2001 From: KaustubhKumar05 Date: Thu, 14 Mar 2024 11:10:35 +0530 Subject: [PATCH 1/4] feat: allow users to submit OpenAI keys --- app/components/RiskyButCoolAPIKeyInput.tsx | 3 +++ app/makeReal.tsx | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/components/RiskyButCoolAPIKeyInput.tsx b/app/components/RiskyButCoolAPIKeyInput.tsx index cf0362e..69d3b24 100644 --- a/app/components/RiskyButCoolAPIKeyInput.tsx +++ b/app/components/RiskyButCoolAPIKeyInput.tsx @@ -9,6 +9,9 @@ export function RiskyButCoolAPIKeyInput() { if (process.env.NODE_ENV === 'development') { localStorage.setItem('makeitreal_key', e.target.value) } + if (typeof window !== 'undefined') { + sessionStorage.setItem('OPEN_AI_KEY', e.target.value) + } }, []) const handleQuestionMessage = useCallback(() => { diff --git a/app/makeReal.tsx b/app/makeReal.tsx index 9d0f373..f90727a 100644 --- a/app/makeReal.tsx +++ b/app/makeReal.tsx @@ -38,9 +38,7 @@ export async function makeReal( // If you're using the API key input, we preference the key from there. // It's okay if this is undefined—it will just mean that we'll use the // one in the .env file instead. - const apiKeyFromDangerousApiKeyInput = ( - document.body.querySelector('#openai_key_risky_but_cool') as HTMLInputElement - )?.value + const apiKeyFromDangerousApiKeyInput = sessionStorage.getItem('OPEN_AI_KEY') // make a request to openai. `fetchFromOpenAi` is a next.js server action, // so our api key is hidden. From 82b35a183949a735151c247da4d60051a214c86c Mon Sep 17 00:00:00 2001 From: KaustubhKumar05 Date: Thu, 14 Mar 2024 11:54:04 +0530 Subject: [PATCH 2/4] fix: show toast for incorrect key --- app/components/JoinForm.tsx | 4 ++++ app/components/RiskyButCoolAPIKeyInput.tsx | 22 +++++++--------------- app/globals.css | 5 +++-- app/lib/fetchFromOpenAi.tsx | 15 ++++++++++----- app/makeReal.tsx | 8 ++++++-- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/app/components/JoinForm.tsx b/app/components/JoinForm.tsx index 430ed95..eba012c 100644 --- a/app/components/JoinForm.tsx +++ b/app/components/JoinForm.tsx @@ -4,6 +4,7 @@ import { useHMSActions } from '@100mslive/react-sdk' import { useState, FormEvent } from 'react' import { useSearchParams } from 'next/navigation' import { ROLES } from './constants' +import { RiskyButCoolAPIKeyInput } from './RiskyButCoolAPIKeyInput' const JoinForm = () => { const [activeTabRole, setActiveTabRole] = useState(ROLES.TEACHER) @@ -111,6 +112,9 @@ const JoinForm = () => { onChange={(e) => setName(e.target.value)} /> + + + {roomCodeParam ? null : (
Join as
diff --git a/app/components/RiskyButCoolAPIKeyInput.tsx b/app/components/RiskyButCoolAPIKeyInput.tsx index 69d3b24..d1ad798 100644 --- a/app/components/RiskyButCoolAPIKeyInput.tsx +++ b/app/components/RiskyButCoolAPIKeyInput.tsx @@ -1,14 +1,11 @@ -import { Icon, useBreakpoint } from '@tldraw/tldraw' import { ChangeEvent, useCallback } from 'react' +import { QuestionIcon } from '@100mslive/react-icons' +import { useBreakpoint } from '@tldraw/tldraw' export function RiskyButCoolAPIKeyInput() { const breakpoint = useBreakpoint() - // Store the API key locally, but ONLY in development mode const handleChange = useCallback((e: ChangeEvent) => { - if (process.env.NODE_ENV === 'development') { - localStorage.setItem('makeitreal_key', e.target.value) - } if (typeof window !== 'undefined') { sessionStorage.setItem('OPEN_AI_KEY', e.target.value) } @@ -21,19 +18,14 @@ export function RiskyButCoolAPIKeyInput() { }, []) return ( -
-
-
- -
+
+
+ Your OpenAI API Key
+
) } diff --git a/app/globals.css b/app/globals.css index a0ccbb9..bed2b01 100644 --- a/app/globals.css +++ b/app/globals.css @@ -167,7 +167,7 @@ h4 { } .input__wrapper:not(:focus-within)::after { - content: 'Your OpenAI API Key (risky but cool)'; + content: 'Enter your OpenAI API Key (risky but cool)'; display: block; position: absolute; inset: 0px; @@ -322,10 +322,11 @@ input::placeholder { } .input-label { + display: flex; font-size: 14px; font-weight: 500; margin-bottom: 5px; - align-items: start; + align-items: center; } .tabs-container { diff --git a/app/lib/fetchFromOpenAi.tsx b/app/lib/fetchFromOpenAi.tsx index 0e18a2e..4967ea6 100644 --- a/app/lib/fetchFromOpenAi.tsx +++ b/app/lib/fetchFromOpenAi.tsx @@ -1,9 +1,11 @@ 'use server' +import { toast } from 'react-toastify' + export async function fetchFromOpenAi( providedApiKey: string, body: GPT4VCompletionRequest -): Promise { +): Promise { const apiKey = providedApiKey ?? process.env.OPENAI_API_KEY if (!apiKey) { @@ -11,9 +13,8 @@ export async function fetchFromOpenAi( 'You need to provide an API key. Make sure OPENAI_API_KEY is set in your .env file.' ) } - try { - const repsonse = await fetch('https://api.openai.com/v1/chat/completions', { + const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -21,11 +22,15 @@ export async function fetchFromOpenAi( }, body: JSON.stringify(body), }) + const jsonData = await response.json() - return await repsonse.json() + if (jsonData.error) { + throw jsonData.error + } + return jsonData } catch (e) { console.error(e) - throw new Error('Sorry, there was an error fetching from OpenAI') + return e.message } } diff --git a/app/makeReal.tsx b/app/makeReal.tsx index f90727a..ccf3de3 100644 --- a/app/makeReal.tsx +++ b/app/makeReal.tsx @@ -7,6 +7,7 @@ import { MessageContent, fetchFromOpenAi, } from './lib/fetchFromOpenAi' +import { toast } from 'react-toastify' const SYSTEM_PROMPT = `You are an expert at constructing interative polls and quizzes for audiences. Your job is to accept a design and turn it into a poll with multiple options for attendees to vote on to make a session more interative. @@ -42,7 +43,7 @@ export async function makeReal( // make a request to openai. `fetchFromOpenAi` is a next.js server action, // so our api key is hidden. - const openAiResponse: GPT4VCompletionResponse = await fetchFromOpenAi( + const openAiResponse: GPT4VCompletionResponse | string = await fetchFromOpenAi( apiKeyFromDangerousApiKeyInput, { model: 'gpt-4-vision-preview', @@ -51,6 +52,9 @@ export async function makeReal( messages: prompt, } ) + if (typeof openAiResponse === 'string') { + throw openAiResponse + } if ('choices' in openAiResponse) { const messageContent = openAiResponse.choices[0].message.content @@ -60,7 +64,7 @@ export async function makeReal( } catch (e) { // if something went wrong, get rid of the unnecessary response shape // editor.deleteShape(responseShapeId) - throw e + toast.error(e) } } From 0c3b34c36b7855cdc0de7d5ae94d21240bfd7fe0 Mon Sep 17 00:00:00 2001 From: KaustubhKumar05 Date: Thu, 14 Mar 2024 11:57:09 +0530 Subject: [PATCH 3/4] fix: make openai key input mandatory --- app/components/RiskyButCoolAPIKeyInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/RiskyButCoolAPIKeyInput.tsx b/app/components/RiskyButCoolAPIKeyInput.tsx index d1ad798..a88958b 100644 --- a/app/components/RiskyButCoolAPIKeyInput.tsx +++ b/app/components/RiskyButCoolAPIKeyInput.tsx @@ -25,7 +25,7 @@ export function RiskyButCoolAPIKeyInput() {
- +
) } From 262f92d249d75c4894b6cab58622042a9ffc5f0b Mon Sep 17 00:00:00 2001 From: KaustubhKumar05 Date: Thu, 14 Mar 2024 11:58:12 +0530 Subject: [PATCH 4/4] fix: remove unused imports --- app/components/RiskyButCoolAPIKeyInput.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/components/RiskyButCoolAPIKeyInput.tsx b/app/components/RiskyButCoolAPIKeyInput.tsx index a88958b..5151a9a 100644 --- a/app/components/RiskyButCoolAPIKeyInput.tsx +++ b/app/components/RiskyButCoolAPIKeyInput.tsx @@ -1,10 +1,7 @@ import { ChangeEvent, useCallback } from 'react' import { QuestionIcon } from '@100mslive/react-icons' -import { useBreakpoint } from '@tldraw/tldraw' export function RiskyButCoolAPIKeyInput() { - const breakpoint = useBreakpoint() - const handleChange = useCallback((e: ChangeEvent) => { if (typeof window !== 'undefined') { sessionStorage.setItem('OPEN_AI_KEY', e.target.value)