├── .env-example ├── .eslintrc.cjs ├── .gitignore ├── .prettierrc.json ├── .vscode └── extensions.json ├── LICENSE.txt ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── ask.svg ├── server.js ├── src ├── App.vue ├── assets │ ├── demo.png │ └── index.css ├── components │ ├── ChatWindowAudio.vue │ ├── ChatWindowImage.vue │ ├── Loading.vue │ ├── Transcriber.vue │ └── Uploader.vue ├── main.js ├── router │ └── index.js ├── stores │ ├── audioChat.js │ ├── imageChat.js │ ├── textChat.js │ └── tokenize.js └── views │ ├── AudioView.vue │ ├── HomeView.vue │ ├── ImageView.vue │ └── TextView.vue ├── tailwind.config.js └── vite.config.js /.env-example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY="" 2 | DG_API="" 3 | REPLICATE="" -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-prettier/skip-formatting' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 'latest' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | node_modules 13 | .DS_Store 14 | dist 15 | dist-ssr 16 | coverage 17 | *.local 18 | 19 | /cypress/videos/ 20 | /cypress/screenshots/ 21 | 22 | # Editor directories and files 23 | .vscode/* 24 | !.vscode/extensions.json 25 | .idea 26 | *.suo 27 | *.ntvs* 28 | *.njsproj 29 | *.sln 30 | *.sw? 31 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sandra Rodgers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI-Powered-App 2 | 3 | ![Screenshot of the AI-Powered App](./src/assets/demo.png) 4 | 5 | This is a project that demonstrates how to use several AI technologies: 6 | 7 | - OpenAI chat completion API 8 | - Deepgram speech-to-text API 9 | - Replicate API to run the miniGPT vision encoder model 10 | - gpt-3-encoder package to tokenize strings into tokens (which are used to set limits on the length of prompts sent to OpenAI) 11 | - Langchain model, chain, and memory interfaces 12 | 13 | ## Get it Working 14 | 15 | To see this project working, you can clone the project and then do the following. 16 | 17 | ### Install dependencies 18 | 19 | ``` 20 | npm install 21 | ``` 22 | 23 | ### Create .env file 24 | 25 | Create a `.env` file at the root of the project and then go into the `.gitignore` file and add `.env` to the list. This will make sure that the `.env` file does not get pushed up to github if you choose to push the project up to github. 26 | 27 | ## API Keys 28 | 29 | Add API keys to the `.env` file as you see in the `.env-example` file. Go to each of these websites to sign up for an API key: 30 | 31 | [OpenAI](https://platform.openai.com/signup) 32 | 33 | [Deepgram](https://dpgr.am/deepgram-signup) 34 | 35 | [Replicate](https://replicate.com/) 36 | 37 | ### Run the web server and the node server 38 | 39 | ``` 40 | npm run start 41 | ``` 42 | 43 | ### To see the Langchain features, switch to the `langchain` branch 44 | 45 | ``` 46 | git checkout langchain 47 | ``` 48 | 49 | ### To see the starter code before the features have been added, switch to the `starting-code` branch 50 | 51 | ``` 52 | git checkout starting-code 53 | ``` 54 | 55 | ### To see starting code and ending code for each chapter, switch to the chapter number + "start" or "end" 56 | 57 | ``` 58 | git checkout 03-start 59 | git checkout 03-end 60 | ``` 61 | 62 | ### Questions 63 | 64 | If you have any questions, you can reach out to me on [twitter](https://twitter.com/sandra_rodgers_) 65 | 66 | # December 2024 Updates 67 | 68 | Updates were made in December 2024 due to breaking changes to the OpenAI, Deepgram, and Replicate APIs. These are explanations of the changes that were made. 69 | 70 | ## Open AI updates 71 | 72 | Code was updated to use the new way of creating the openai client. You can see a diff at https://github.com/SandraRodgers/Ask-AI/commit/9cce3610593391d45739966072224b812f1d2232. 73 | 74 | These updates were made to branches `main`, `langchain`, `03-end`, `04-start`, `04-end`, `05-start`, `05-end`, `06-start`, `06-end`, `07-start`, `07-end`, `08-start`, `08-end`, `09-start`, `09-end` (so all branches except for `03-start`) 75 | 76 | Lesson 3 in the video course will need to be updated to present this new way of creating the openai client.There is also one example in lesson two that presents a snippet of the code (see the section "Request Prompt Format") 77 | 78 | ### Code changes 79 | 80 | 1. Updated to version `^4.71.1` 81 | 2. Updated openai config in `server.js`: 82 | 83 | ```js 84 | ////// OpenAI config ////// 85 | const { OpenAI } = require('openai') 86 | 87 | const client = new OpenAI({ 88 | apiKey: process.env.OPENAI_API_KEY 89 | }) 90 | 91 | // OpenAI chat completion 92 | app.post('/chat', async (req, res) => { 93 | const messages = req.body.messages 94 | console.log(messages) 95 | try { 96 | if (messages == null) { 97 | throw new Error('We have a problem - no prompt was provided') 98 | } 99 | 100 | const response = await client.chat.completions.create({ 101 | model: 'gpt-3.5-turbo', 102 | messages 103 | }) 104 | const completion = response.choices[0].message 105 | console.dir(response.choices[0], { depth: 4 }) 106 | return res.status(200).json({ 107 | success: true, 108 | message: completion 109 | }) 110 | } catch (error) { 111 | console.log(error.message) 112 | } 113 | }) 114 | ``` 115 | 116 | ## Deepgram updates 117 | 118 | Code was updated to use the new way of creating the deepgram client. You can see a diff at https://github.com/SandraRodgers/Ask-AI/commit/e2df40f043662ff44747f8d938f41b540ee1d52b 119 | 120 | These updates were made to branches `main`, `langchain`,`05-end`, `06-start`, `06-end`, `07-start`, `07-end`, `08-start`, `08-end`, `09-start`, `09-end` (so all branches except for `03-start`, `04-start`, `04-end`) 121 | 122 | Lesson 5 in the video course will need to be updated to present this new way of creating the deepgram client. 123 | 124 | ### Code changes 125 | 126 | 1. Updated to version `^3.9.0` 127 | 2. Updated deepgram config in `server.js`: 128 | 129 | ```js 130 | ////// Deepgram config ////// 131 | const { createClient } = require('@deepgram/sdk') 132 | const deepgram = createClient(process.env.DG_API) 133 | 134 | // Deepgram transcription 135 | app.post('/dg-transcription', upload.single('file'), async (req, res) => { 136 | try { 137 | console.log(req.file) 138 | if (!req.file) { 139 | return res.status(400).send('No file uploaded') 140 | } 141 | 142 | const audioBuffer = req.file.buffer 143 | 144 | const response = await deepgram.listen.prerecorded.transcribeFile(audioBuffer, { 145 | punctuate: true, 146 | model: 'nova-2' 147 | }) 148 | 149 | console.dir(response, { depth: 4 }) 150 | 151 | res.send({ transcript: response.result }) 152 | } catch (e) { 153 | console.error('Error:', e) 154 | res.status(500).send('An error occurred during transcription') 155 | } 156 | }) 157 | ``` 158 | 159 | ## Langchain updates 160 | 161 | Code was updated to use the new way of creating the langchain-openai client (just two lines of code). You can see a diff at https://github.com/SandraRodgers/Ask-AI/commit/55b2c88ae3701647ca224376bc11bdbca41ff381 162 | 163 | These updates were made to branches `langchain`, `07-end`, `08-start`, `08-end`, `09-start`, `09-end` 164 | 165 | Lesson 7 in the video course will need to be updated to present this new way of creating the langchain-openai client. However, it's a very small change. 166 | 167 | The change adds `const { OpenAI: OpenAIClient } = require('@langchain/openai')`, and the reason that we add the `: OpenAIClient` is so that we can use a different name from the openai client used higher up in the same file. Thus when we instantiate the model, we can do: `const model = new OpenAIClient({}) ` 168 | 169 | ### Code changes 170 | 171 | 1. Updated to version `^0.0.299` 172 | 2. Updated `server.js` 173 | 174 | ```js 175 | ////// LangChain Config ////// 176 | const { OpenAI: OpenAIClient } = require('@langchain/openai') 177 | const { BufferMemory } = require('langchain/memory') 178 | const { ConversationChain } = require('langchain/chains') 179 | 180 | // Specify the model here 181 | const model = new OpenAIClient({}) 182 | const memory = new BufferMemory() 183 | const chain = new ConversationChain({ llm: model, memory: memory }) 184 | let chainNum = 0 185 | ``` 186 | 187 | ## Replicate updates 188 | 189 | Code was updated because the model id for minigpt-4 had changed. See a diff at: https://github.com/SandraRodgers/Ask-AI/commit/0775a8537d046475877c8b85b8d9c1e34ab2633d 190 | 191 | These updates were made to branches `main`, `08-end`, `09-start`, `09-end`. 192 | 193 | Lesson 8 and 9 in the video course (server.js section) will need to be updated with the new model id. 194 | 195 | ### Code changes 196 | 197 | 1. Update `server.js` 198 | 199 | ```js 200 | const miniGPT = 201 | 'daanelson/minigpt-4:e447a8583cffd86ce3b93f9c2cd24f2eae603d99ace6afa94b33a08e94a3cd06' 202 | ``` 203 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ask AI 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-powered-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "description": "A Node.js and Vue.js app that uses the OpenAI API and other AI technologies to generate text based on user input.", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "preview": "vite preview", 11 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", 12 | "format": "prettier --write src/", 13 | "init-server": "node server.js", 14 | "start": "npm-run-all --parallel dev init-server" 15 | }, 16 | "dependencies": { 17 | "@deepgram/sdk": "^3.9.0", 18 | "@vueuse/core": "^10.1.2", 19 | "autoprefixer": "^10.4.14", 20 | "body-parser": "^1.20.2", 21 | "cors": "^2.8.5", 22 | "dotenv": "^16.0.3", 23 | "express": "^4.18.2", 24 | "gpt-3-encoder": "^1.1.4", 25 | "multer": "^1.4.5-lts.1", 26 | "npm-run-all": "^4.1.5", 27 | "openai": "^4.71.1", 28 | "pinia": "^2.0.36", 29 | "postcss": "^8.4.23", 30 | "replicate": "^0.12.1", 31 | "tailwindcss": "^3.3.2", 32 | "vue": "^3.3.2", 33 | "vue-router": "^4.2.0" 34 | }, 35 | "devDependencies": { 36 | "@rushstack/eslint-patch": "^1.2.0", 37 | "@vitejs/plugin-vue": "^4.2.3", 38 | "@vue/eslint-config-prettier": "^7.1.0", 39 | "eslint": "^8.39.0", 40 | "eslint-plugin-vue": "^9.11.0", 41 | "prettier": "^2.8.8", 42 | "vite": "^4.3.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/ask.svg: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const envConfig = require('dotenv').config() 2 | const express = require('express') 3 | const cors = require('cors') 4 | const bodyParser = require('body-parser') 5 | 6 | const multer = require('multer') 7 | const upload = multer() 8 | 9 | const port = 3000 10 | const app = express() 11 | app.use(cors()) 12 | app.use(bodyParser.json()) 13 | 14 | ////// OpenAI config ////// 15 | const { OpenAI } = require('openai') 16 | 17 | const client = new OpenAI({ 18 | apiKey: process.env.OPENAI_API_KEY 19 | }) 20 | 21 | // OpenAI chat completion 22 | app.post('/chat', async (req, res) => { 23 | const messages = req.body.messages 24 | console.log(messages) 25 | try { 26 | if (messages == null) { 27 | throw new Error('We have a problem - no prompt was provided') 28 | } 29 | 30 | const response = await client.chat.completions.create({ 31 | model: 'gpt-3.5-turbo', 32 | messages 33 | }) 34 | const completion = response.choices[0].message 35 | console.dir(response.choices[0], { depth: 4 }) 36 | return res.status(200).json({ 37 | success: true, 38 | message: completion 39 | }) 40 | } catch (error) { 41 | console.log(error.message) 42 | } 43 | }) 44 | 45 | ////// Deepgram config ////// 46 | const { createClient } = require('@deepgram/sdk') 47 | const deepgram = createClient(process.env.DG_API) 48 | 49 | // Deepgram transcription 50 | app.post('/dg-transcription', upload.single('file'), async (req, res) => { 51 | try { 52 | console.log(req.file) 53 | if (!req.file) { 54 | return res.status(400).send('No file uploaded') 55 | } 56 | 57 | const audioBuffer = req.file.buffer 58 | 59 | const response = await deepgram.listen.prerecorded.transcribeFile(audioBuffer, { 60 | punctuate: true, 61 | model: 'nova-2' 62 | }) 63 | 64 | console.dir(response, { depth: 4 }) 65 | 66 | res.send({ transcript: response.result }) 67 | } catch (e) { 68 | console.error('Error:', e) 69 | res.status(500).send('An error occurred during transcription') 70 | } 71 | }) 72 | 73 | ////// Replicate config ////// 74 | const Replicate = require('replicate') 75 | const replicate = new Replicate({ 76 | auth: process.env.REPLICATE 77 | }) 78 | 79 | const miniGPT = 80 | 'daanelson/minigpt-4:e447a8583cffd86ce3b93f9c2cd24f2eae603d99ace6afa94b33a08e94a3cd06' 81 | 82 | // Replicate (minigpt) image analyzer 83 | app.post('/minigpt', async (req, res) => { 84 | try { 85 | const miniGPTResponse = await replicate.run(miniGPT, { 86 | input: { 87 | image: req.body.image, 88 | prompt: req.body.prompt 89 | } 90 | }) 91 | res.send({ message: miniGPTResponse }) 92 | } catch (e) { 93 | console.log('error', e) 94 | } 95 | }) 96 | 97 | ////// Token Counter ////// 98 | const { encode } = require('gpt-3-encoder') 99 | 100 | // Token Counter 101 | app.post('/tokenize', async (req, res) => { 102 | const str = req.body.stringToTokenize 103 | try { 104 | if (str == null) { 105 | throw new Error('No string was provided') 106 | } 107 | const encoded = encode(str) 108 | const length = encoded.length 109 | console.log('Token count is ' + length) 110 | return res.status(200).json({ 111 | success: true, 112 | tokens: length 113 | }) 114 | } catch (error) { 115 | console.log(error.message) 116 | } 117 | }) 118 | 119 | app.listen(port, () => console.log(`Example app listening on port ${port}!`)) 120 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | -------------------------------------------------------------------------------- /src/assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SandraRodgers/Ask-AI/3a6dfcc258d4c57c50efae620ec2fd1f0e8df757/src/assets/demo.png -------------------------------------------------------------------------------- /src/assets/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | background-color: #101014; 7 | } 8 | 9 | body { 10 | background-color: #101014; 11 | } 12 | 13 | #app { 14 | background-color: #101014; 15 | color: #e9e5df; 16 | font-weight: normal; 17 | } 18 | 19 | nav { 20 | background-image: linear-gradient( 21 | to right, 22 | #2a013d, 23 | #101014 3%, 24 | #101014, 25 | #101014, 26 | #101014, 27 | #2a013d 110% 28 | ); 29 | color: #e9e5df; 30 | @apply border-b border-purple-400 font-bold; 31 | @apply w-screen; 32 | @apply py-4 px-10; 33 | @apply flex justify-between; 34 | } 35 | 36 | main { 37 | margin: 0 auto; 38 | @apply max-w-md sm:max-w-xl md:max-w-2xl lg:max-w-5xl xl:max-w-7xl; 39 | @apply pt-10; 40 | } 41 | 42 | article { 43 | @apply h-[650px] lg:h-[564px]; 44 | } 45 | 46 | .button, 47 | button[type='submit'] { 48 | @apply flex items-center justify-center text-center; 49 | @apply py-1 px-6; 50 | @apply rounded-md; 51 | @apply h-8 min-w-min; 52 | @apply cursor-pointer; 53 | } 54 | 55 | .button-primary, 56 | button[type='submit'] { 57 | @apply text-[#e9e5df] font-semibold; 58 | @apply border-[#42b983] border; 59 | } 60 | 61 | .chat-button:hover { 62 | @apply bg-[#42b983]; 63 | } 64 | 65 | .chat-button span:hover { 66 | @apply bg-[#42b983]; 67 | } 68 | 69 | .button-primary:hover { 70 | @apply text-white; 71 | @apply bg-[#42b983]; 72 | } 73 | 74 | .button-secondary { 75 | @apply border-[#7686FF] border text-[#e9e5df] font-bold; 76 | } 77 | 78 | .button-secondary:hover { 79 | @apply bg-[#658efd] text-[#eef1fb]; 80 | } 81 | 82 | .button-large { 83 | @apply py-8 px-6; 84 | @apply text-2xl text-[#e9e5df]; 85 | @apply border-[#42b983] border; 86 | } 87 | 88 | .button-large:hover { 89 | @apply py-8 px-6; 90 | @apply text-2xl text-white; 91 | @apply bg-[#42b983]; 92 | } 93 | 94 | .question-input { 95 | @apply block w-full rounded-none rounded-l-md border-0 py-1.5 text-gray-100 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 sm:text-sm sm:leading-6; 96 | } 97 | 98 | input { 99 | background-color: #1a1a21; 100 | color: #e9e5df; 101 | @apply border-slate-500 border rounded-sm; 102 | @apply h-8; 103 | @apply p-2; 104 | } 105 | 106 | textarea { 107 | color: #e9e5df; 108 | background-color: #1a1a21; 109 | } 110 | 111 | h1 { 112 | @apply text-[3rem] xl:text-2xl; 113 | @apply font-bold; 114 | } 115 | 116 | h2 { 117 | font-size: 2rem; 118 | font-weight: 500; 119 | } 120 | 121 | h3 { 122 | /* font-weight: 700; */ 123 | /* @apply mb-4; */ 124 | } 125 | -------------------------------------------------------------------------------- /src/components/ChatWindowAudio.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 61 | -------------------------------------------------------------------------------- /src/components/ChatWindowImage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 63 | -------------------------------------------------------------------------------- /src/components/Loading.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 30 | -------------------------------------------------------------------------------- /src/components/Transcriber.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /src/components/Uploader.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 53 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import './assets/index.css' 2 | 3 | import { createApp } from 'vue' 4 | import { createPinia } from 'pinia' 5 | 6 | import App from './App.vue' 7 | import router from './router' 8 | 9 | const app = createApp(App) 10 | 11 | app.use(createPinia()) 12 | app.use(router) 13 | 14 | app.mount('#app') 15 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import HomeView from '../views/HomeView.vue' 3 | import TextView from '../views/TextView.vue' 4 | import AudioView from '../views/AudioView.vue' 5 | import ImageView from '../views/ImageView.vue' 6 | import { useTokenizeStore } from '../stores/tokenize' 7 | 8 | const router = createRouter({ 9 | history: createWebHistory(import.meta.env.BASE_URL), 10 | routes: [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | component: HomeView 15 | }, 16 | { 17 | path: '/text', 18 | name: 'text', 19 | component: TextView 20 | }, 21 | { 22 | path: '/audio', 23 | name: 'audio', 24 | component: AudioView 25 | }, 26 | { 27 | path: '/image', 28 | name: 'image', 29 | component: ImageView 30 | } 31 | ] 32 | }) 33 | 34 | router.beforeEach(() => { 35 | const tokenizeStore = useTokenizeStore() 36 | tokenizeStore.tokenLength = 0 37 | }) 38 | 39 | export default router 40 | -------------------------------------------------------------------------------- /src/stores/audioChat.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | import { useTokenizeStore } from './tokenize' 4 | 5 | export const useAudioChatStore = defineStore('audioChat', () => { 6 | const tokenizeStore = useTokenizeStore() 7 | const file = ref({}) 8 | const prompt = ref([]) 9 | const gptResponse = ref('') 10 | const transcript = ref('') 11 | const question = ref('') 12 | const isTranscribing = ref(false) 13 | const isLoadingGPT = ref(false) 14 | const clearFile = ref(false) 15 | const questionAnswerList = ref([]) 16 | 17 | function transcribeFile() { 18 | if (file.value === 0) { 19 | alert('Please attach a file') 20 | } else { 21 | const formData = new FormData() 22 | formData.append('file', file.value.value) 23 | isTranscribing.value = true 24 | fetch('http://localhost:3000/dg-transcription', { 25 | method: 'POST', 26 | body: formData 27 | }) 28 | .then((response) => response.json()) 29 | .then((data) => { 30 | transcript.value = data.transcript.results.channels[0].alternatives[0].transcript 31 | file.value = {} 32 | 33 | isTranscribing.value = false 34 | }) 35 | } 36 | } 37 | 38 | function createPrompt() { 39 | prompt.value = [] 40 | const instructions = { 41 | role: 'system', 42 | content: 43 | 'You will answer questions about the following text that has been transcribed from an audio file.' 44 | } 45 | const transcriptToAnalyze = { role: 'user', content: transcript.value } 46 | const chatQuestion = { role: 'user', content: question.value } 47 | ///create prompt array 48 | prompt.value.push(instructions) 49 | prompt.value.push(transcriptToAnalyze) 50 | prompt.value.push(chatQuestion) 51 | 52 | tokenizeStore.checkTokens( 53 | instructions.content + transcriptToAnalyze.content + chatQuestion.content 54 | ) 55 | 56 | if (transcript.value) { 57 | sendPrompt() 58 | } else { 59 | alert('Please transcribe an audio file.') 60 | prompt.value = [] 61 | } 62 | } 63 | 64 | function sendPrompt() { 65 | isLoadingGPT.value = true 66 | 67 | fetch('http://localhost:3000/chat', { 68 | method: 'POST', 69 | body: JSON.stringify({ 70 | messages: prompt.value 71 | }), 72 | headers: { 73 | 'Content-Type': 'application/json' 74 | } 75 | }) 76 | .then((response) => response.json()) 77 | .then((data) => { 78 | isLoadingGPT.value = false 79 | gptResponse.value = data.message.content 80 | // array to save the conversation 81 | questionAnswerList.value.push({ 82 | question: question.value, 83 | answer: data.message.content 84 | }) 85 | question.value = '' 86 | }) 87 | } 88 | 89 | function clearChat() { 90 | file.value = {} 91 | prompt.value = [] 92 | gptResponse.value = '' 93 | transcript.value = '' 94 | question.value = '' 95 | isTranscribing.value = false 96 | isLoadingGPT.value = false 97 | tokenizeStore.tokenLength = 0 98 | clearFile.value = true 99 | questionAnswerList.value = [] 100 | } 101 | 102 | return { 103 | prompt, 104 | createPrompt, 105 | sendPrompt, 106 | gptResponse, 107 | file, 108 | transcribeFile, 109 | transcript, 110 | question, 111 | isTranscribing, 112 | isLoadingGPT, 113 | clearChat, 114 | clearFile, 115 | questionAnswerList 116 | } 117 | }) 118 | -------------------------------------------------------------------------------- /src/stores/imageChat.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | import { useTokenizeStore } from './tokenize' 4 | 5 | export const useImageChatStore = defineStore('imageChat', () => { 6 | const tokenizeStore = useTokenizeStore() 7 | const prompt = ref([]) 8 | const miniGPTResponse = ref('') 9 | const question = ref('') 10 | const isThinking = ref(false) 11 | const imageURL = ref('') 12 | const questionAnswerList = ref([]) 13 | 14 | function createPrompt() { 15 | isThinking.value = true 16 | tokenizeStore.checkTokens(imageURL.value + question.value) 17 | fetch('http://localhost:3000/minigpt', { 18 | method: 'POST', 19 | body: JSON.stringify({ 20 | image: imageURL.value, 21 | prompt: question.value 22 | }), 23 | headers: { 24 | 'Content-Type': 'application/json' 25 | } 26 | }) 27 | .then((response) => response.json()) 28 | .then((data) => { 29 | console.log(data) 30 | miniGPTResponse.value = data.message 31 | questionAnswerList.value.push({ 32 | question: question.value, 33 | answer: data.message 34 | }) 35 | isThinking.value = false 36 | question.value = '' 37 | }) 38 | } 39 | 40 | function clearChat() { 41 | prompt.value = [] 42 | miniGPTResponse.value = '' 43 | question.value = '' 44 | isThinking.value = false 45 | imageURL.value = '' 46 | questionAnswerList.value = [] 47 | } 48 | 49 | return { 50 | prompt, 51 | miniGPTResponse, 52 | createPrompt, 53 | isThinking, 54 | imageURL, 55 | question, 56 | clearChat, 57 | questionAnswerList 58 | } 59 | }) 60 | -------------------------------------------------------------------------------- /src/stores/textChat.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | import { useTokenizeStore } from './tokenize' 4 | 5 | export const useTextChatStore = defineStore('textChat', () => { 6 | const tokenizeStore = useTokenizeStore() 7 | const text = ref('') 8 | const question = ref('') 9 | const prompt = ref([]) 10 | const gptResponse = ref('') 11 | const isLoadingGPT = ref(false) 12 | 13 | function createPrompt() { 14 | // prompt items 15 | const instructions = { 16 | role: 'system', 17 | content: 'You will answer a question about the following text.' 18 | } 19 | const textToAnalyze = { role: 'user', content: text.value } 20 | const chatQuestion = { role: 'user', content: question.value } 21 | 22 | // create prompt array 23 | prompt.value.push(instructions) 24 | prompt.value.push(textToAnalyze) 25 | prompt.value.push(chatQuestion) 26 | tokenizeStore.checkTokens(instructions.content + textToAnalyze.content + chatQuestion.content) 27 | } 28 | 29 | function sendPrompt() { 30 | if (text.value.length === 0) { 31 | alert('You have not added any text to analyze.') 32 | } else { 33 | isLoadingGPT.value = true 34 | 35 | fetch('http://localhost:3000/chat', { 36 | method: 'POST', 37 | body: JSON.stringify({ 38 | messages: prompt.value 39 | }), 40 | headers: { 41 | 'Content-Type': 'application/json' 42 | } 43 | }) 44 | .then((response) => response.json()) 45 | .then((data) => { 46 | isLoadingGPT.value = false 47 | gptResponse.value = data.message.content 48 | }) 49 | } 50 | } 51 | 52 | function clearChat() { 53 | text.value = '' 54 | question.value = '' 55 | prompt.value = [] 56 | gptResponse.value = '' 57 | isLoadingGPT.value = false 58 | tokenizeStore.tokenLength = 0 59 | } 60 | 61 | return { text, question, prompt, createPrompt, sendPrompt, gptResponse, isLoadingGPT, clearChat } 62 | }) 63 | -------------------------------------------------------------------------------- /src/stores/tokenize.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useTokenizeStore = defineStore('tokenize', () => { 5 | const tokenLength = ref(0) 6 | 7 | function checkTokens(val) { 8 | tokenLength.value = 0 9 | fetch('http://localhost:3000/tokenize', { 10 | method: 'POST', 11 | body: JSON.stringify({ 12 | stringToTokenize: val 13 | }), 14 | headers: { 15 | 'Content-Type': 'application/json' 16 | } 17 | }) 18 | .then((response) => response.json()) 19 | .then((data) => { 20 | tokenLength.value = data.tokens 21 | }) 22 | .catch((error) => { 23 | console.log(error) 24 | }) 25 | } 26 | return { checkTokens, tokenLength } 27 | }) 28 | -------------------------------------------------------------------------------- /src/views/AudioView.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | -------------------------------------------------------------------------------- /src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /src/views/ImageView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | -------------------------------------------------------------------------------- /src/views/TextView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 |