From 5d8578b77afd3b665dd1a1eaa15cb9d5e9d07c7c Mon Sep 17 00:00:00 2001 From: Fernando Navarro Date: Thu, 16 Mar 2023 23:34:52 -0700 Subject: [PATCH 1/3] ChatGPT-3.5-Turbo chatbox display + API --- app/(user)/page.tsx | 4 +- components/ChatGPTAssistant.tsx | 131 +++++++++++++++++++++++++++++++ package-lock.json | 132 ++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 components/ChatGPTAssistant.tsx diff --git a/app/(user)/page.tsx b/app/(user)/page.tsx index 106727f..7b31794 100644 --- a/app/(user)/page.tsx +++ b/app/(user)/page.tsx @@ -1,6 +1,5 @@ 'use client'; -// import { MutableRefObject, useRef, useState } from 'react'; -// import axios from 'axios'; +import ChatGPTAssistant from '../../components/ChatGPTAssistant'; import { ImageGenerator } from '../../components/ImageGenerator'; export default function Home() { @@ -16,6 +15,7 @@ export default function Home() { }} /> + ); } diff --git a/components/ChatGPTAssistant.tsx b/components/ChatGPTAssistant.tsx new file mode 100644 index 0000000..1c3da5d --- /dev/null +++ b/components/ChatGPTAssistant.tsx @@ -0,0 +1,131 @@ +import { useState } from 'react'; +// import './App.css'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; +import { + MainContainer, + ChatContainer, + MessageList, + Message, + MessageInput, + TypingIndicator, +} from '@chatscope/chat-ui-kit-react'; + +const systemMessage = { + role: 'system', + content: "Explain all concepts like I'm a junior/mid-level web developer", +}; + +const API_KEY = process.env.NEXT_PUBLIC_OPENAI_API_KEY; + +function ChatGPTAssistant() { + const [typing, setTyping] = useState(false); + const [messages, setMessages] = useState([ + { + message: "Hello, I'm ChatGPT! Ask me anything!", + sentTime: 'just now', + sender: 'ChatGPT', + }, + ]); + + const handleSend = async (message) => { + const newMessage = { + message, + direction: 'outgoing', + sender: 'user', + }; + + const newMessages = [...messages, newMessage]; + + setMessages(newMessages); + + setTyping(true); + + await processMessageToChatGPT(newMessages); + }; + + async function processMessageToChatGPT(chatMessages) { + // messages is an array of messages + // Format messages for chatGPT API + // API is expecting objects in format of { role: "user" or "assistant", "content": "message here"} + // So we need to reformat + + let apiMessages = chatMessages.map((messageObject) => { + let role = ''; + if (messageObject.sender === 'ChatGPT') { + role = 'assistant'; + } else { + role = 'user'; + } + return { role: role, content: messageObject.message }; + }); + + // Get the request body set up with the model we plan to use + // and the messages which we formatted above. We add a system message in the front to' + // determine how we want chatGPT to act. + const apiRequestBody = { + model: 'gpt-3.5-turbo', + messages: [ + systemMessage, // The system message DEFINES the logic of our chatGPT + ...apiMessages, // The messages from our chat with ChatGPT + ], + }; + + try { + const response = await fetch( + 'https://api.openai.com/v1/chat/completions', + { + method: 'POST', + headers: { + Authorization: 'Bearer ' + API_KEY, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(apiRequestBody), + } + ); + + if (!response.ok) { + throw new Error('Failed to fetch data from the ChatGPT API'); + } + + const data = await response.json(); + console.log(data); + setMessages([ + ...chatMessages, + { + message: data.choices[0].message.content, + sender: 'ChatGPT', + }, + ]); + setTyping(false); + } catch (error) { + console.error(error); + setTyping(false); + } + } + + return ( +
+
+ + + : null + } + > + {messages.map((message, i) => { + return ; + })} + + + + +
+
+ ); +} + +export default ChatGPTAssistant; diff --git a/package-lock.json b/package-lock.json index 3144044..aeffdf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@chatscope/chat-ui-kit-react": "^1.10.1", "@portabletext/react": "^2.0.2", "@sanity/image-url": "^1.0.2", "@sanity/vision": "^3.0.0", @@ -1852,6 +1853,30 @@ "node": ">=6.9.0" } }, + "node_modules/@chatscope/chat-ui-kit-react": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-react/-/chat-ui-kit-react-1.10.1.tgz", + "integrity": "sha512-pJIxvM9zR2oPk601P/S3Du1lZZSQcv76+8/IUhhWo88oy1iqiw2yk71T3nzE0sr8kRfZ8FoSSxOjVx9j7amjow==", + "dependencies": { + "@chatscope/chat-ui-kit-styles": "^1.2.0", + "@fortawesome/fontawesome-free": "^5.12.1", + "@fortawesome/fontawesome-svg-core": "^1.2.26", + "@fortawesome/free-solid-svg-icons": "^5.12.0", + "@fortawesome/react-fontawesome": "^0.1.8", + "classnames": "^2.2.6", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "prop-types": "^15.7.2", + "react": "^16.12.0 || ^17.0.0 || ^18.2.0", + "react-dom": "^16.12.0 || ^17.0.0 || ^18.2.0" + } + }, + "node_modules/@chatscope/chat-ui-kit-styles": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-styles/-/chat-ui-kit-styles-1.4.0.tgz", + "integrity": "sha512-016mBJD3DESw7Nh+lkKcPd22xG92ghA0VpIXIbjQtmXhC7Ve6wRazTy8z1Ahut+Tbv179+JxrftuMngsj/yV8Q==" + }, "node_modules/@codemirror/autocomplete": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz", @@ -2494,6 +2519,60 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", + "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.x" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -12323,6 +12402,25 @@ "to-fast-properties": "^2.0.0" } }, + "@chatscope/chat-ui-kit-react": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-react/-/chat-ui-kit-react-1.10.1.tgz", + "integrity": "sha512-pJIxvM9zR2oPk601P/S3Du1lZZSQcv76+8/IUhhWo88oy1iqiw2yk71T3nzE0sr8kRfZ8FoSSxOjVx9j7amjow==", + "requires": { + "@chatscope/chat-ui-kit-styles": "^1.2.0", + "@fortawesome/fontawesome-free": "^5.12.1", + "@fortawesome/fontawesome-svg-core": "^1.2.26", + "@fortawesome/free-solid-svg-icons": "^5.12.0", + "@fortawesome/react-fontawesome": "^0.1.8", + "classnames": "^2.2.6", + "prop-types": "^15.7.2" + } + }, + "@chatscope/chat-ui-kit-styles": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-styles/-/chat-ui-kit-styles-1.4.0.tgz", + "integrity": "sha512-016mBJD3DESw7Nh+lkKcPd22xG92ghA0VpIXIbjQtmXhC7Ve6wRazTy8z1Ahut+Tbv179+JxrftuMngsj/yV8Q==" + }, "@codemirror/autocomplete": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz", @@ -12710,6 +12808,40 @@ "@floating-ui/dom": "^1.1.0" } }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" + }, + "@fortawesome/fontawesome-free": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", + "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "requires": { + "prop-types": "^15.8.1" + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", diff --git a/package.json b/package.json index 8fb7bb4..353559e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "start": "next start" }, "dependencies": { + "@chatscope/chat-ui-kit-react": "^1.10.1", "@portabletext/react": "^2.0.2", "@sanity/image-url": "^1.0.2", "@sanity/vision": "^3.0.0", From ee6e25c69e073bcc3e414d94011d97833101125a Mon Sep 17 00:00:00 2001 From: Fernando Navarro Date: Fri, 17 Mar 2023 10:42:03 -0700 Subject: [PATCH 2/3] App styling improvements --- app/(user)/page.tsx | 33 ++++++++--- components/ChatGPTAssistant.tsx | 55 +++++++++++++------ ...geGenerator.tsx => ImageGeneratorForm.tsx} | 27 ++++----- 3 files changed, 76 insertions(+), 39 deletions(-) rename components/{ImageGenerator.tsx => ImageGeneratorForm.tsx} (93%) diff --git a/app/(user)/page.tsx b/app/(user)/page.tsx index 7b31794..e1f545c 100644 --- a/app/(user)/page.tsx +++ b/app/(user)/page.tsx @@ -1,19 +1,38 @@ 'use client'; +import { useState } from 'react'; import ChatGPTAssistant from '../../components/ChatGPTAssistant'; -import { ImageGenerator } from '../../components/ImageGenerator'; +import { ImageGeneratorForm } from '../../components/ImageGeneratorForm'; export default function Home() { + const [imageUrl, setImageUrl] = useState(''); + return (

- Generate Image with Text + Generate Images with Text

- +
+
+ { + setImageUrl(generatedImageUrl); + }} + /> +
+
+ {imageUrl && ( +
+

Generated Image:

+ Generated +
+ )} +
+
diff --git a/components/ChatGPTAssistant.tsx b/components/ChatGPTAssistant.tsx index 1c3da5d..47da5f0 100644 --- a/components/ChatGPTAssistant.tsx +++ b/components/ChatGPTAssistant.tsx @@ -27,6 +27,15 @@ function ChatGPTAssistant() { }, ]); + const [inputValue, setInputValue] = useState(''); + + const handleSubmit = async (event) => { + event.preventDefault(); + if (inputValue.trim() === '') return; + await handleSend(inputValue); + setInputValue(''); + }; + const handleSend = async (message) => { const newMessage = { message, @@ -104,26 +113,38 @@ function ChatGPTAssistant() { } return ( -
-
- - - : null +
+
ChatGPT Assistant
+ +
+ {messages.map((message, index) => ( +
+ - {messages.map((message, i) => { - return ; - })} - - - - + {message.sender === 'user' ? 'You: ' : 'Assistant: '} + + {message.message} +
+ ))}
+ +
+ setInputValue(e.target.value)} + /> + +
); } diff --git a/components/ImageGenerator.tsx b/components/ImageGeneratorForm.tsx similarity index 93% rename from components/ImageGenerator.tsx rename to components/ImageGeneratorForm.tsx index 85001c0..bff257d 100644 --- a/components/ImageGenerator.tsx +++ b/components/ImageGeneratorForm.tsx @@ -1,11 +1,11 @@ import { MutableRefObject, useRef, useState } from 'react'; import axios from 'axios'; -interface ImageGeneratorProps { +interface ImageGeneratorFormProps { onGenerate: (imageUrl: string) => void; } -export const ImageGenerator: React.FC = ({ +export const ImageGeneratorForm: React.FC = ({ onGenerate, }) => { // State variables @@ -57,7 +57,9 @@ export const ImageGenerator: React.FC = ({ prompt: prompt, options: options, }); - setImageUrl(response.data.output[0]); + const generatedImageUrl = response.data.output[0]; + setImageUrl(generatedImageUrl); + onGenerate(generatedImageUrl); } catch (error) { console.error('Error:', error); } @@ -71,7 +73,7 @@ export const ImageGenerator: React.FC = ({ e.preventDefault(); handleGenerateImage(); }} - className="grid grid-cols-1 md:grid-cols-2 gap-4" + className="grid grid-cols-1 gap-4" >