diff --git a/.gitignore b/.gitignore index 00840df40b..fa70c3c4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ bower_components/ cache.json api/data/ .eslintrc.js -owner*.yml +owner.yml archive .vscode/settings.json diff --git a/README.md b/README.md index 45adcf4788..d0ca06a9ca 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Here are my recently completed and planned features: ^* ChatGPT can be 'customized' by setting a system message or prompt prefix and alternate 'role' to the API request -[More info here](https://platform.openai.com/docs/guides/chat/instructing-chat-models). Here's an [example from this app.](#use-cases) +[More info here](https://platform.openai.com/docs/guides/chat/instructing-chat-models). Here's an [example from this app.]() ### Tech Stack @@ -126,8 +126,8 @@ Here are my recently completed and planned features: - Node.js >= 19.0.0 - MongoDB installed or [MongoDB Atlas](https://account.mongodb.com/account/login) (required if not using Docker) - [Docker (optional)](https://www.docker.com/get-started/) -- [OpenAI API key](https://platform.openai.com/account/api-keys) (optional: chats will not be titled and only free models will work) -- BingAI, ChatGPT access tokens (optional for free AI models) +- [OpenAI API key](https://platform.openai.com/account/api-keys) +- BingAI, ChatGPT access tokens (optional, free AIs) ## Usage @@ -136,7 +136,6 @@ Here are my recently completed and planned features: git clone https://github.com/danny-avila/chatgpt-clone.git ``` - If using MongoDB Atlas, remove `&w=majority` from default connection string. -- You can configure proxy, host, and port environment variables as needed ### Local - **Run npm** install in both the api and client directories @@ -162,7 +161,12 @@ Here are my recently completed and planned features:
ChatGPT Free Instructions -To get your Access token For ChatGPT 'Free Version', login to chat.openai.com, then visit https://chat.openai.com/api/auth/session. Set it to `CHATGPT_TOKEN` in `.env` or `docker-compose.yml` + + +**This has been disabled as is no longer working as of 3-07-23** + + +To get your Access token For ChatGPT 'Free Version', login to chat.openai.com, then visit https://chat.openai.com/api/auth/session. **Warning:** There may be a high chance of your account being banned with this method. Continue doing so at your own risk. @@ -171,7 +175,7 @@ To get your Access token For ChatGPT 'Free Version', login to chat.openai.com, t
BingAI Instructions -The Bing Access Token is the "_U" cookie from bing.com. Use dev tools or an extension while logged into the site to view it. Set it in `.env` or `docker-compose.yml` to `BING_TOKEN`; if setting the user token doesn't work, try setting the entire cookie string from bing request header to `BING_COOKIES` +The Bing Access Token is the "_U" cookie from bing.com. Use dev tools or an extension while logged into the site to view it. **Note:** Specific error handling and styling for this model is still in progress.
diff --git a/api/app/bingai.js b/api/app/bingai.js index d1fc07e28b..4dfa8a71d5 100644 --- a/api/app/bingai.js +++ b/api/app/bingai.js @@ -2,32 +2,27 @@ require('dotenv').config(); const { KeyvFile } = require('keyv-file'); const askBing = async ({ text, progressCallback, convo }) => { - const { BingAIClient } = await import('@waylaidwanderer/chatgpt-api'); + const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api')); - const clientOptions = { + const bingAIClient = new BingAIClient({ + // "_U" cookie from bing.com userToken: process.env.BING_TOKEN, + // If the above doesn't work, provide all your cookies as a string instead + // cookies: '', debug: false, cache: { store: new KeyvFile({ filename: './data/cache.json' }) } - }; - - const cookies = process.env.BING_COOKIES; - - if (cookies?.length > 0) { - clientOptions.cookies = cookies; - delete clientOptions.userToken; - } - - const bingAIClient = new BingAIClient(clientOptions); + }); let options = { - onProgress: async (partialRes) => await progressCallback(partialRes) + onProgress: async (partialRes) => await progressCallback(partialRes), }; if (convo) { options = { ...options, ...convo }; } - const res = await bingAIClient.sendMessage(text, options); + const res = await bingAIClient.sendMessage(text, options + ); return res; diff --git a/api/app/chatgpt-browser.js b/api/app/chatgpt-browser.js index a17f277822..442d2a731d 100644 --- a/api/app/chatgpt-browser.js +++ b/api/app/chatgpt-browser.js @@ -7,7 +7,6 @@ const clientOptions = { // Access token from https://chat.openai.com/api/auth/session accessToken: process.env.CHATGPT_TOKEN, // debug: true - proxy: process.env.PROXY || null, }; const browserClient = async ({ text, progressCallback, convo }) => { diff --git a/api/app/chatgpt-client.js b/api/app/chatgpt-client.js index afd31e0a81..ce3c0e2271 100644 --- a/api/app/chatgpt-client.js +++ b/api/app/chatgpt-client.js @@ -5,7 +5,6 @@ const clientOptions = { modelOptions: { model: 'gpt-3.5-turbo' }, - proxy: process.env.PROXY || null, debug: false }; diff --git a/api/app/chatgpt-custom.js b/api/app/chatgpt-custom.js index a356ba4b1a..d31901c75f 100644 --- a/api/app/chatgpt-custom.js +++ b/api/app/chatgpt-custom.js @@ -5,7 +5,6 @@ const clientOptions = { modelOptions: { model: 'gpt-3.5-turbo' }, - proxy: process.env.PROXY || null, debug: false }; diff --git a/api/app/sydney.js b/api/app/sydney.js index cb2f83fc7e..fe47c74f57 100644 --- a/api/app/sydney.js +++ b/api/app/sydney.js @@ -2,39 +2,30 @@ require('dotenv').config(); const { KeyvFile } = require('keyv-file'); const askSydney = async ({ text, progressCallback, convo }) => { - const { BingAIClient } = await import('@waylaidwanderer/chatgpt-api'); + const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api')); - const clientOptions = { + const sydneyClient = new BingAIClient({ + // "_U" cookie from bing.com userToken: process.env.BING_TOKEN, + // If the above doesn't work, provide all your cookies as a string instead + // cookies: '', debug: false, cache: { store: new KeyvFile({ filename: './data/cache.json' }) } - }; - - const cookies = process.env.BING_COOKIES; - - if (cookies?.length > 0) { - clientOptions.cookies = cookies; - delete clientOptions.userToken; - } - - const sydneyClient = new BingAIClient(clientOptions); + }); let options = { jailbreakConversationId: true, - onProgress: async (partialRes) => await progressCallback(partialRes) + onProgress: async (partialRes) => await progressCallback(partialRes), }; if (convo.parentMessageId) { - options = { - ...options, - jailbreakConversationId: convo.jailbreakConversationId, - parentMessageId: convo.parentMessageId - }; + options = { ...options, jailbreakConversationId: convo.jailbreakConversationId, parentMessageId: convo.parentMessageId }; } console.log('sydney options', options); - const res = await sydneyClient.sendMessage(text, options); + const res = await sydneyClient.sendMessage(text, options + ); return res; diff --git a/api/app/titleConvo.js b/api/app/titleConvo.js index 6b064b4b08..f793075d98 100644 --- a/api/app/titleConvo.js +++ b/api/app/titleConvo.js @@ -1,32 +1,24 @@ const { Configuration, OpenAIApi } = require('openai'); const titleConvo = async ({ message, response, model }) => { - try { - const configuration = new Configuration({ - apiKey: process.env.OPENAI_KEY - }); - const openai = new OpenAIApi(configuration); - const completion = await openai.createChatCompletion({ - model: 'gpt-3.5-turbo', - messages: [ - { - role: 'system', - content: - 'You are a title-generator with one job: titling the conversation provided by a user in title case.' - }, - { - role: 'user', - content: `In 5 words or less, summarize the conversation below with a title in title case. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: ` - } - ] - }); + const configuration = new Configuration({ + apiKey: process.env.OPENAI_KEY + }); + const openai = new OpenAIApi(configuration); + const completion = await openai.createChatCompletion({ + model: 'gpt-3.5-turbo', + messages: [ + { + role: 'system', + content: + 'You are a title-generator with one job: titling the conversation provided by a user in title case.' + }, + { role: 'user', content: `In 5 words or less, summarize the conversation below with a title in title case. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: ` }, + ] + }); - //eslint-disable-next-line - return completion.data.choices[0].message.content.replace(/["\.]/g, ''); - } catch (error) { - console.log(error); - return 'New Chat'; - } + //eslint-disable-next-line + return completion.data.choices[0].message.content.replace(/["\.]/g, ''); }; module.exports = titleConvo; diff --git a/api/server/index.js b/api/server/index.js index be2feffd89..347491f0af 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -1,29 +1,28 @@ -const express = require('express'); -const dbConnect = require('../models/dbConnect'); -const path = require('path'); -const cors = require('cors'); -const routes = require('./routes'); -const app = express(); -const port = process.env.PORT || 3080; -const host = process.env.HOST || 'localhost' -const projectPath = path.join(__dirname, '..', '..', 'client'); -dbConnect().then(() => console.log('Connected to MongoDB')); - -app.use(cors()); -app.use(express.json()); -app.use(express.static(path.join(projectPath, 'public'))); - -app.get('/', function (req, res) { - console.log(path.join(projectPath, 'public', 'index.html')); - res.sendFile(path.join(projectPath, 'public', 'index.html')); -}); - -app.use('/api/ask', routes.ask); -app.use('/api/messages', routes.messages); -app.use('/api/convos', routes.convos); -app.use('/api/customGpts', routes.customGpts); -app.use('/api/prompts', routes.prompts); - -app.listen(port, host, () => { - console.log(`Server listening at http://${host}:${port}`); -}); +const express = require('express'); +const dbConnect = require('../models/dbConnect'); +const path = require('path'); +const cors = require('cors'); +const routes = require('./routes'); +const app = express(); +const port = process.env.PORT || 3080; +const projectPath = path.join(__dirname, '..', '..', 'client'); +dbConnect().then(() => console.log('Connected to MongoDB')); + +app.use(cors()); +app.use(express.json()); +app.use(express.static(path.join(projectPath, 'public'))); + +app.get('/', function (req, res) { + console.log(path.join(projectPath, 'public', 'index.html')); + res.sendFile(path.join(projectPath, 'public', 'index.html')); +}); + +app.use('/api/ask', routes.ask); +app.use('/api/messages', routes.messages); +app.use('/api/convos', routes.convos); +app.use('/api/customGpts', routes.customGpts); +app.use('/api/prompts', routes.prompts); + +app.listen(port, () => { + console.log(`Server listening at http://localhost:${port}`); +}); \ No newline at end of file diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index ce4cf3c6be..745671e1cc 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -23,8 +23,8 @@ export default function Conversation({ const { modelMap } = useSelector((state) => state.models); const inputRef = useRef(null); const dispatch = useDispatch(); - const { trigger } = manualSWR(`/api/messages/${id}`, 'get'); - const rename = manualSWR(`/api/convos/update`, 'post'); + const { trigger } = manualSWR(`http://localhost:3080/api/messages/${id}`, 'get'); + const rename = manualSWR(`http://localhost:3080/api/convos/update`, 'post'); const clickHandler = async () => { if (conversationId === id) { diff --git a/client/src/components/Conversations/DeleteButton.jsx b/client/src/components/Conversations/DeleteButton.jsx index 9b8e688cd0..03c9f47f81 100644 --- a/client/src/components/Conversations/DeleteButton.jsx +++ b/client/src/components/Conversations/DeleteButton.jsx @@ -9,7 +9,7 @@ import { setMessages } from '~/store/messageSlice'; export default function DeleteButton({ conversationId, renaming, cancelHandler }) { const dispatch = useDispatch(); const { trigger } = manualSWR( - `/api/convos/clear`, + `http://localhost:3080/api/convos/clear`, 'post', () => { dispatch(setMessages([])); diff --git a/client/src/components/Messages/TabLink.jsx b/client/src/components/Messages/TabLink.jsx deleted file mode 100644 index e095f85a6c..0000000000 --- a/client/src/components/Messages/TabLink.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -export default function TabLink(a) { - return ( - - {a.children} - - ); -} diff --git a/client/src/components/Messages/TextWrapper.jsx b/client/src/components/Messages/TextWrapper.jsx index 28b8f6480c..d4e528695e 100644 --- a/client/src/components/Messages/TextWrapper.jsx +++ b/client/src/components/Messages/TextWrapper.jsx @@ -2,22 +2,10 @@ import React from 'react'; import Markdown from 'markdown-to-jsx'; import Embed from './Embed'; import Highlight from './Highlight'; -import TabLink from './TabLink'; import regexSplit from '~/utils/regexSplit'; import { wrapperRegex } from '~/utils'; const { codeRegex, inLineRegex, markupRegex, languageMatch, newLineMatch } = wrapperRegex; -const mdOptions = { - wrapper: React.Fragment, - forceWrapper: true, - overrides: { - a: { - component: TabLink, - // props: { - // className: 'foo' - // } - } - } -}; +const mdOptions = { wrapper: React.Fragment, forceWrapper: true }; const inLineWrap = (parts) => { let previousElement = null; diff --git a/client/src/components/Models/ModelDialog.jsx b/client/src/components/Models/ModelDialog.jsx index a1b05dfcdd..47592d8a93 100644 --- a/client/src/components/Models/ModelDialog.jsx +++ b/client/src/components/Models/ModelDialog.jsx @@ -25,7 +25,7 @@ export default function ModelDialog({ mutate, setModelSave, handleSaveState }) { const [saveText, setSaveText] = useState('Save'); const [required, setRequired] = useState(false); const inputRef = useRef(null); - const updateCustomGpt = manualSWR(`/api/customGpts/`, 'post'); + const updateCustomGpt = manualSWR(`http://localhost:3080/api/customGpts/`, 'post'); const submitHandler = (e) => { if (chatGptLabel.length === 0) { diff --git a/client/src/components/Models/ModelItem.jsx b/client/src/components/Models/ModelItem.jsx index 494b20a2ac..8b7d8d082d 100644 --- a/client/src/components/Models/ModelItem.jsx +++ b/client/src/components/Models/ModelItem.jsx @@ -15,8 +15,8 @@ export default function ModelItem({ modelName, value, onSelect }) { const [currentName, setCurrentName] = useState(modelName); const [modelInput, setModelInput] = useState(modelName); const inputRef = useRef(null); - const rename = manualSWR(`/api/customGpts`, 'post'); - const deleteCustom = manualSWR(`/api/customGpts/delete`, 'post'); + const rename = manualSWR(`http://localhost:3080/api/customGpts`, 'post'); + const deleteCustom = manualSWR(`http://localhost:3080/api/customGpts/delete`, 'post'); if (value === 'chatgptCustom') { return ( diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx index 94e9dd5c86..607b48921f 100644 --- a/client/src/components/Models/ModelMenu.jsx +++ b/client/src/components/Models/ModelMenu.jsx @@ -27,7 +27,7 @@ export default function ModelMenu() { const [menuOpen, setMenuOpen] = useState(false); const { model, customModel } = useSelector((state) => state.submit); const { models, modelMap, initial } = useSelector((state) => state.models); - const { trigger } = manualSWR(`/api/customGpts`, 'get', (res) => { + const { trigger } = manualSWR(`http://localhost:3080/api/customGpts`, 'get', (res) => { const fetchedModels = res.map((modelItem) => ({ ...modelItem, name: modelItem.chatGptLabel diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx index e6402b0c40..d262226bb9 100644 --- a/client/src/components/Nav/ClearConvos.jsx +++ b/client/src/components/Nav/ClearConvos.jsx @@ -10,10 +10,10 @@ export default function ClearConvos() { const dispatch = useDispatch(); const { mutate } = useSWRConfig(); - const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => { + const { trigger } = manualSWR(`http://localhost:3080/api/convos/clear`, 'post', () => { dispatch(setMessages([])); dispatch(setNewConvo()); - mutate(`/api/convos`); + mutate(`http://localhost:3080/api/convos`); }); const clickHandler = () => { diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index fa74e069e9..2ae96d22cc 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -17,7 +17,7 @@ export default function Nav() { }; const { data, isLoading, mutate } = swr( - `/api/convos?pageNumber=${pageNumber}`, + `http://localhost:3080/api/convos?pageNumber=${pageNumber}`, onSuccess ); const containerRef = useRef(null); diff --git a/client/src/utils/handleSubmit.js b/client/src/utils/handleSubmit.js index 4ba262ac08..09e1aa52b6 100644 --- a/client/src/utils/handleSubmit.js +++ b/client/src/utils/handleSubmit.js @@ -11,7 +11,7 @@ export default function handleSubmit({ chatGptLabel, promptPrefix }) { - const endpoint = `/api/ask`; + const endpoint = `http://localhost:3080/api/ask`; let payload = { model, text, chatGptLabel, promptPrefix }; if (convo.conversationId && convo.parentMessageId) { payload = { diff --git a/docker-compose.yml b/docker-compose.yml index fbb079235a..83fa98fd21 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,12 +22,6 @@ services: - OPENAI_KEY="" - CHATGPT_TOKEN="" - BING_TOKEN="" - # if setting the user token doesn't work, try setting the entire cookie string from bing request header - - BING_COOKIES="" - # Change this to proxy any request. - - PROXY="" - # Change this if you need to configure host name, default: 'localhost' - - HOST="" ports: - "9000:3080" volumes: