Merge branch 'easy-config-setup' of https://github.com/danny-avila/chatgpt-clone into easy-config-setup

This commit is contained in:
Daniel Avila 2023-03-06 16:59:03 -05:00
commit b9e9203a75
127 changed files with 8902 additions and 7282 deletions

11
.gitignore vendored
View file

@ -1,6 +1,7 @@
### node etc ###
# Logs
data-node
logs
*.log
@ -24,11 +25,16 @@ dist/
public/main.js
public/main.js.map
public/main.js.LICENSE.txt
client/public/main.js
client/public/main.js.map
client/public/main.js.LICENSE.txt
# Dependency directorys
# Deployed apps should consider commenting these lines out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules/
api/node_modules/
client/node_modules/
bower_components/
# Floobits
@ -40,7 +46,10 @@ bower_components/
# Environment
.env
cache.json
data/
api/data/
.eslintrc.js
owner.yml
archive
.vscode/settings.json
src/style - official.css

13
.vscode/settings.json vendored
View file

@ -1,13 +0,0 @@
{
"editor.lightbulb.enabled": false,
"editor.parameterHints.enabled": false,
"editor.renderWhitespace": "all",
// "editor.snippetSuggestions": "none",
"editor.tabSize": 2,
"editor.wordWrap": "on",
"emmet.showExpandedAbbreviation": "never",
"files.trimTrailingWhitespace": true,
"javascript.suggest.enabled": false,
"javascript.updateImportsOnFileMove.enabled": "never",
"javascript.validate.enable": true
}

View file

@ -1,10 +1,14 @@
# ChatGPT Clone #
![chatgpt-clone demo](./public/demo.gif)
## Wrap all conversational AIs under one roof. ##
Assistant AIs are the future and OpenAI revolutionized this movement with ChatGPT. While numerous methods exist to integrate these AIs, this app commemorates the original styling of ChatGPT, with the ability to integrate any current/future AI models through user-provided API keys, while improving upon original client features, such as conversation search and prompt templates. This project was built with the anticipation of the official ChatGPT API from OpenAI, and now uses it along with the free access method. Through this clone, you can avoid subscription-based models in favor of free or pay-per-call APIs. I will most likely not deploy this app, as it's mainly a learning experience, but feel free to clone or fork to create your own custom wrapper.
Assistant AIs are the future and OpenAI revolutionized this movement with ChatGPT. While numerous methods exist to integrate these AIs, this app commemorates the original styling of ChatGPT, with the ability to integrate any current/future AI models, while improving upon original client features, such as conversation search and prompt templates (currently WIP). This project was started early in Feb '23, anticipating the release of the official ChatGPT API from OpenAI, and now uses it along with access to the free version. Through this clone, you can avoid subscription-based models (ChatGPT Plus) in favor of free or pay-per-call APIs. I will soon deploy a demo of this app. Feel free to contribute, clone, or fork. Currently dockerized.
## Updates
<details open>
<summary><strong>2023-03-06</strong></summary>
Due to increased interest in the repo, I've dockerized the app as of this update for quick setup! See setup instructions below. I realize this still takes some time with installing docker dependencies, so it's on the roadmap to have a deployed demo. Besides this, I've made major improvements for a lot of the existing features across the board, mainly UI/UX.
</details>
<details>
<summary><strong>2023-03-04</strong></summary>
Custom prompt prefixing and labeling is now supported through the official API. This nets some interesting results when you need ChatGPT for specific uses or entertainment. Select 'CustomGPT' in the model menu to configure this, and you can choose to save the configuration or reference it by conversation. Model selection will change by conversation.
</details>
@ -35,48 +39,61 @@ Currently, this project is only functional with the `text-davinci-003` model.
</details>
</details>
# Table of Contents
* [Roadmap](#roadmap)
* [Features](#features)
* [Tech Stack](#tech-stack)
* [Getting Started](#getting-started)
* [Prerequisites](#prerequisites)
* [Usage](#usage)
<!-- * [Module](#module)
* [API Server](#api-server)
* [CLI](#cli) -->
* [Using a Reverse Proxy](#using-a-reverse-proxy)
* [Caveats](#caveats)
* [Contributing](#contributing)
* [License](#license)
## Roadmap
> **Warning**
> This is a work in progress. I'm building this in public. You can follow the progress here or on my [Linkedin](https://www.linkedin.com/in/danny-avila).
Here are my planned/recently finished features.
Here are my recently completed and planned features:
- [x] Rename, delete conversations
- [x] Persistent conversation
- [x] Rename, delete conversations
- [x] UI Error handling
- [x] AI Model Selection
- [x] Bing AI integration
- [x] Remember last selected model
- [x] Highlight.js for code blocks
- [x] AI model change handling (start new convos within existing, remembers last selected)
- [x] Code block handling (highlighting, markdown, clipboard, language detection)
- [x] Markdown handling
- [x] Language Detection for code blocks
- [x] 'Copy to clipboard' button for code blocks
- [x] Customize prompt prefix/label (custom ChatGPT using official API)
- [x] AI model change handling (start new convos within existing convo)
- [x] Server convo pagination (limit fetch and load more with 'show more' button)
- [ ] Config file for easy startup
- [x] Config file for easy startup (docker compose)
- [ ] Conversation Search (by title)
- [ ] Resubmit/edit sent messages
- [ ] Semantic Search Option (requires more tokens)
- [ ] Bing AI Styling (for suggested responses, convo end, etc.)
- [ ] Prompt Templates/Search
- [ ] Refactor/clean up code (tech debt)
- [ ] Optional use of local storage for credentials
- [ ] Mobile styling (half-finished)
- [ ] Deploy demo
### Features
- Response streaming identical to ChatGPT
- Response streaming identical to ChatGPT through server-sent events
- UI from original ChatGPT, including Dark mode
- AI model selection, including OpenAI's official ChatGPT API
### Tech Stack
- Utilizes [node-chatgpt-api](https://github.com/waylaidwanderer/node-chatgpt-api)
- Response streaming identical to ChatGPT through server-sent events
- Use of Tailwind CSS (like the official site) and [shadcn/ui](https://github.com/shadcn/ui) components
- highlight.js, useSWR, Redux, Express, MongoDB, [Keyv](https://www.npmjs.com/package/keyv)
- No React boilerplate/toolchain, created from scratch with react@latest
- Use of Tailwind CSS and [shadcn/ui](https://github.com/shadcn/ui) components
- Docker, useSWR, Redux, Express, MongoDB, [Keyv](https://www.npmjs.com/package/keyv)
## Use Cases ##
@ -100,10 +117,37 @@ Here are my planned/recently finished features.
Serves and searches all conversations reliably. All AI convos under one house.
Pay per call and not per month (cents compared to dollars). -->
## How to Get Started ##
> **Warning**
> Working on easy startup/config code. Still in development.
## Getting Started
<!-- ## License
### Prerequisites
- Node.js >= 19.0.0
- npm
- [Docker (optional)](https://www.docker.com/get-started/)
- [OpenAI API key](https://platform.openai.com/account/api-keys)
- BingAI, ChatGPT access tokens (optional, free AIs)
Licensed under the [insert license here](). -->
## Usage
<details open>
<summary><strong>BingAI Instructions</strong></summary>
<details open>
<summary><strong>ChatGPT Free Instructions</strong></summary>
See [`demos/use-browser-client.js`](demos/use-browser-client.js).
</details>
## Caveats
### Regarding `ChatGPTClient`
From @waylaidwanderer's use of the official API: Since `gpt-3.5-turbo` is ChatGPT's underlying model, I had to do my best to replicate the way the official ChatGPT website uses it.
This means my implementation or the underlying model may not behave exactly the same in some ways:
- Conversations are not tied to any user IDs, so if that's important to you, you should implement your own user ID system.
- ChatGPT's model parameters (temperature, frequency penalty, etc.) are unknown, so I set some defaults that I thought would be reasonable.
- Conversations are limited to roughly the last 3000 tokens, so earlier messages may be forgotten during longer conversations.
- This works in a similar way to ChatGPT, except I'm pretty sure they have some additional way of retrieving context from earlier messages when needed (which can probably be achieved with embeddings, but I consider that out-of-scope for now).
## Contributing
If you'd like to contribute, please create a pull request with a detailed description of your changes.
## License
This project is licensed under the MIT License.

2
api/.dockerignore Normal file
View file

@ -0,0 +1,2 @@
/node_modules
.env

6
api/.env.example Normal file
View file

@ -0,0 +1,6 @@
OPENAI_KEY=
PORT=3080
NODE_ENV=development
MONGO_URI="mongodb://127.0.0.1:27017/chatgpt-client"
CHATGPT_TOKEN=""
BING_TOKEN=""

14
api/DockerFile Normal file
View file

@ -0,0 +1,14 @@
FROM node:19-alpine
WORKDIR /api
# copy package.json into the container at /api
COPY package*.json /api/
# install dependencies
RUN npm install
# Copy the current directory contents into the container at /api
COPY . /api/
# Make port 3080 available to the world outside this container
EXPOSE 3080
# Run the app when the container launches
CMD ["npm", "start"]
# docker build -t node-api .

View file

@ -10,7 +10,7 @@ const askBing = async ({ text, progressCallback, convo }) => {
// If the above doesn't work, provide all your cookies as a string instead
// cookies: '',
debug: false,
store: new KeyvFile({ filename: './data/cache.json' })
store: new KeyvFile({ filename: './api/data/cache.json' })
});
let options = {

View file

@ -12,7 +12,7 @@ const browserClient = async ({ text, progressCallback, convo }) => {
const { ChatGPTBrowserClient } = await import('@waylaidwanderer/chatgpt-api');
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
store: new KeyvFile({ filename: './api/data/cache.json' })
};
const client = new ChatGPTBrowserClient(clientOptions, store);

View file

@ -11,7 +11,7 @@ const clientOptions = {
const askClient = async ({ text, progressCallback, convo }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
store: new KeyvFile({ filename: './api/data/cache.json' })
};
const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store);

View file

@ -11,7 +11,7 @@ const clientOptions = {
const customClient = async ({ text, progressCallback, convo, promptPrefix, chatGptLabel }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
store: new KeyvFile({ filename: './api/data/cache.json' })
};
clientOptions.chatGptLabel = chatGptLabel;

54
api/app/detectCode.js Normal file
View file

@ -0,0 +1,54 @@
const { ModelOperations } = require('@vscode/vscode-languagedetection');
const codeRegex = /(```[\s\S]*?```)/g;
const languageMatch = /```(\w+)/;
const detectCode = async (text) => {
try {
if (!text.match(codeRegex)) {
// console.log('disqualified for non-code match')
return text;
}
if (text.match(languageMatch)) {
// console.log('disqualified for language match')
return text;
}
// console.log('qualified for code match');
const modelOperations = new ModelOperations();
const regexSplit = (await import('./regexSplit.mjs')).default;
const parts = regexSplit(text, codeRegex);
const output = parts.map(async (part) => {
if (part.match(codeRegex)) {
const code = part.slice(3, -3);
const language = await modelOperations.runModel(code);
return part.replace(/^```/, `\`\`\`${language[0].languageId}`);
} else {
// return i > 0 ? '\n' + part : part;
return part;
}
});
return (await Promise.all(output)).join('');
} catch (e) {
console.log('Error in detectCode function\n', e);
return text;
}
};
// const example3 = {
// text: "By default, the function generates an 8-character password with uppercase and lowercase letters and digits, but no special characters.\n\nTo use this function, simply call it with the desired arguments. For example:\n\n```\n>>> generate_password()\n'wE5pUxV7'\n>>> generate_password(length=12, special_chars=True)\n'M4v&^gJ*8#qH'\n>>> generate_password(uppercase=False, digits=False)\n'zajyprxr'\n``` \n\nNote that the randomness is used to select characters from the available character sets, but the resulting password is always deterministic given the same inputs. This makes the function useful for generating secure passwords that meet specific requirements."
// };
// const example4 = {
// text: 'here\'s a cool function:\n```\nimport random\nimport string\n\ndef generate_password(length=8, uppercase=True, lowercase=True, digits=True, special_chars=False):\n """Generate a random password with specified requirements.\n\n Args:\n length (int): The length of the password. Default is 8.\n uppercase (bool): Whether to include uppercase letters. Default is True.\n lowercase (bool): Whether to include lowercase letters. Default is True.\n digits (bool): Whether to include digits. Default is True.\n special_chars (bool): Whether to include special characters. Default is False.\n\n Returns:\n str: A random password with the specified requirements.\n """\n # Define character sets to use in password generation\n chars = ""\n if uppercase:\n chars += string.ascii_uppercase\n if lowercase:\n chars += string.ascii_lowercase\n if digits:\n chars += string.digits\n if special_chars:\n chars += string.punctuation\n\n # Generate the password\n password = "".join(random.choice(chars) for _ in range(length))\n return password\n```\n\nThis function takes several arguments'
// };
// write an immediately invoked function to test this
// (async () => {
// const result = await detectCode(example3.text);
// console.log(result);
// })();
module.exports = detectCode;

7893
api/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

37
api/package.json Normal file
View file

@ -0,0 +1,37 @@
{
"name": "chatgpt-clone",
"version": "1.0.0",
"description": "",
"main": "server/index.js",
"scripts": {
"start": "npx node server/index.js",
"server-dev": "npx nodemon server/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/danny-avila/chatgpt-clone.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/danny-avila/chatgpt-clone/issues"
},
"homepage": "https://github.com/danny-avila/chatgpt-clone#readme",
"dependencies": {
"@keyv/mongo": "^2.1.8",
"@vscode/vscode-languagedetection": "^1.0.22",
"@waylaidwanderer/chatgpt-api": "^1.15.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"mongoose": "^6.9.0",
"openai": "^3.1.0"
},
"devDependencies": {
"nodemon": "^2.0.20",
"path": "^0.12.7"
}
}

View file

@ -4,8 +4,8 @@ const path = require('path');
const cors = require('cors');
const routes = require('./routes');
const app = express();
const port = 3050;
const projectPath = path.join(__dirname, '..');
const port = process.env.PORT || 3050;
const projectPath = path.join(__dirname, '..', '..', 'client');
dbConnect().then(() => console.log('Connected to MongoDB'));
app.use(cors());
@ -17,11 +17,11 @@ app.get('/', function (req, res) {
res.sendFile(path.join(projectPath, 'public', 'index.html'));
});
app.use('/ask', routes.ask);
app.use('/messages', routes.messages);
app.use('/convos', routes.convos);
app.use('/customGpts', routes.customGpts);
app.use('/prompts', routes.prompts);
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}`);

View file

@ -1,54 +0,0 @@
const { ModelOperations } = require('@vscode/vscode-languagedetection');
const codeRegex = /(```[\s\S]*?```)/g;
const languageMatch = /```(\w+)/;
const detectCode = async (text) => {
try {
if (!text.match(codeRegex)) {
// console.log('disqualified for non-code match')
return text;
}
if (text.match(languageMatch)) {
// console.log('disqualified for language match')
return text;
}
// console.log('qualified for code match');
const modelOperations = new ModelOperations();
const regexSplit = (await import('../src/utils/regexSplit.mjs')).default;
const parts = regexSplit(text, codeRegex);
const output = parts.map(async (part, i) => {
if (part.match(codeRegex)) {
const code = part.slice(3, -3);
const language = await modelOperations.runModel(code);
return part.replace(/^```/, `\`\`\`${language[0].languageId}`);
} else {
// return i > 0 ? '\n' + part : part;
return part;
}
});
return (await Promise.all(output)).join('');
} catch (e) {
console.log('Error in detectCode function\n', e);
return text;
}
};
const example3 = {
text: "By default, the function generates an 8-character password with uppercase and lowercase letters and digits, but no special characters.\n\nTo use this function, simply call it with the desired arguments. For example:\n\n```\n>>> generate_password()\n'wE5pUxV7'\n>>> generate_password(length=12, special_chars=True)\n'M4v&^gJ*8#qH'\n>>> generate_password(uppercase=False, digits=False)\n'zajyprxr'\n``` \n\nNote that the randomness is used to select characters from the available character sets, but the resulting password is always deterministic given the same inputs. This makes the function useful for generating secure passwords that meet specific requirements."
};
const example4 = {
text: 'here\'s a cool function:\n```\nimport random\nimport string\n\ndef generate_password(length=8, uppercase=True, lowercase=True, digits=True, special_chars=False):\n """Generate a random password with specified requirements.\n\n Args:\n length (int): The length of the password. Default is 8.\n uppercase (bool): Whether to include uppercase letters. Default is True.\n lowercase (bool): Whether to include lowercase letters. Default is True.\n digits (bool): Whether to include digits. Default is True.\n special_chars (bool): Whether to include special characters. Default is False.\n\n Returns:\n str: A random password with the specified requirements.\n """\n # Define character sets to use in password generation\n chars = ""\n if uppercase:\n chars += string.ascii_uppercase\n if lowercase:\n chars += string.ascii_lowercase\n if digits:\n chars += string.digits\n if special_chars:\n chars += string.punctuation\n\n # Generate the password\n password = "".join(random.choice(chars) for _ in range(length))\n return password\n```\n\nThis function takes several arguments'
};
// write an immediately invoked function to test this
// (async () => {
// const result = await detectCode(example3.text);
// console.log(result);
// })();
module.exports = detectCode;

2
client/.dockerignore Normal file
View file

@ -0,0 +1,2 @@
/node_modules
.env

22
client/Dockerfile Normal file
View file

@ -0,0 +1,22 @@
# Stage 1
FROM node:19-alpine as builder
WORKDIR /client
# copy package.json into the container at /client
COPY package*.json /client/
# install dependencies
RUN npm install
# Copy the current directory contents into the container at /client
COPY . /client/
# Run the app when the container launches
CMD ["npm", "run", "build"]
# Stage 2
FROM nginx:stable-alpine
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
COPY --from=builder /client/public /usr/share/nginx/html
# Add your nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# docker build -t react-client .

15
client/nginx.conf Normal file
View file

@ -0,0 +1,15 @@
server {
listen 80;
server_name localhost;
location / {
# Serve your React app
root /usr/share/nginx/html;
index index.html;
}
location /api {
# Proxy requests to the API service
proxy_pass http://api:3080/api;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,56 +1,42 @@
{
"name": "rpp2210-mvp",
"name": "chatgpt-clone",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server .",
"build": "Webpack .",
"server": "npx node server/index.js",
"build-dev": "Webpack . --watch",
"server-dev": "npx nodemon server/index.js",
"test": "test"
"build": "webpack",
"build-dev": "Webpack . --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/danny-avila/rpp2210-mvp.git"
"url": "git+https://github.com/danny-avila/chatgpt-clone.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/danny-avila/rpp2210-mvp/issues"
"url": "https://github.com/danny-avila/chatgpt-clone/issues"
},
"homepage": "https://github.com/danny-avila/rpp2210-mvp#readme",
"homepage": "https://github.com/danny-avila/chatgpt-clone#readme",
"dependencies": {
"@keyv/mongo": "^2.1.8",
"@radix-ui/react-alert-dialog": "^1.0.2",
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-dropdown-menu": "^2.0.2",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-tabs": "^1.0.2",
"@reduxjs/toolkit": "^1.9.2",
"@vscode/vscode-languagedetection": "^1.0.22",
"@waylaidwanderer/chatgpt-api": "^1.15.1",
"chatgpt": "^4.2.0",
"axios": "^1.3.4",
"class-variance-authority": "^0.4.0",
"clsx": "^1.2.1",
"cors": "^2.8.5",
"crypto-browserify": "^3.12.0",
"dotenv": "^16.0.3",
"highlight.js": "^11.7.0",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"lucide-react": "^0.113.0",
"markdown-to-jsx": "^7.1.9",
"mongoose": "^6.9.0",
"openai": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-textarea-autosize": "^8.4.0",
"react-transition-group": "^4.4.5",
"remark-supersub": "^1.0.0",
"swr": "^2.0.3",
"tailwind-merge": "^1.9.1",
"tailwindcss-animate": "^1.0.5",
@ -75,8 +61,6 @@
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"html-webpack-plugin": "^5.5.0",
"nodemon": "^2.0.20",
"path": "^0.12.7",
"postcss": "^8.4.21",
"postcss-loader": "^7.0.2",

View file

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 828 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 432 KiB

After

Width:  |  Height:  |  Size: 432 KiB

Before After
Before After

View file

@ -23,8 +23,8 @@ export default function Conversation({
const { modelMap } = useSelector((state) => state.models);
const inputRef = useRef(null);
const dispatch = useDispatch();
const { trigger } = manualSWR(`http://localhost:3050/messages/${id}`, 'get');
const rename = manualSWR(`http://localhost:3050/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) {

View file

@ -9,7 +9,7 @@ import { setMessages } from '~/store/messageSlice';
export default function DeleteButton({ conversationId, renaming, cancelHandler }) {
const dispatch = useDispatch();
const { trigger } = manualSWR(
'http://localhost:3050/convos/clear',
`http://localhost:3080/api/convos/clear`,
'post',
() => {
dispatch(setMessages([]));

View file

@ -2,7 +2,7 @@ import React from 'react';
import RenameIcon from '../svg/RenameIcon';
import CheckMark from '../svg/CheckMark';
export default function RenameButton({ onClick, renaming, renameHandler, onRename }) {
export default function RenameButton({ renaming, renameHandler, onRename }) {
const handler = renaming ? onRename : renameHandler;
return (

View file

@ -56,16 +56,21 @@ export default function Message({
if (notUser) {
props.className =
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]';
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]';
}
if (notUser && backgroundColor || sender === 'bingai') {
if ((notUser && backgroundColor) || sender === 'bingai') {
icon = (
<div
style={{ backgroundColor }}
className="relative flex h-[30px] w-[30px] items-center justify-center rounded-sm p-1 text-white"
>
{sender === 'bingai' ? <BingIcon /> : <GPTIcon />}
{error && (
<span className="absolute right-0 top-[20px] -mr-2 flex h-4 w-4 items-center justify-center rounded-full border border-white bg-red-500 text-[10px] text-white">
!
</span>
)}
</div>
);
}

View file

@ -23,7 +23,7 @@ export default function ModelDialog({ mutate, modelMap }) {
const [saveText, setSaveText] = useState('Save');
const [required, setRequired] = useState(false);
const inputRef = useRef(null);
const updateCustomGpt = manualSWR('http://localhost:3050/customGpts/', 'post');
const updateCustomGpt = manualSWR(`http://localhost:3080/api/customGpts/`, 'post');
const submitHandler = (e) => {
if (chatGptLabel.length === 0) {

View file

@ -25,8 +25,7 @@ export default function ModelMenu() {
const dispatch = useDispatch();
const { model, customModel } = useSelector((state) => state.submit);
const { models, modelMap, initial } = useSelector((state) => state.models);
const { trigger } = manualSWR('http://localhost:3050/customGpts', 'get', (res) => {
console.log('models data (response)', res);
const { trigger } = manualSWR(`http://localhost:3080/api/customGpts`, 'get', (res) => {
if (models.length + res.length === models.length) {
return;
}

View file

@ -11,12 +11,12 @@ export default function ClearConvos() {
const { mutate } = useSWRConfig()
const { trigger } = manualSWR(
'http://localhost:3050/convos/clear',
`http://localhost:3080/api/convos/clear`,
'post',
() => {
dispatch(setMessages([]));
dispatch(setConversation({ error: false, title: 'New chat', conversationId: null, parentMessageId: null }));
mutate('http://localhost:3050/convos');
mutate(`http://localhost:3080/api/convos`);
}
);

View file

@ -17,7 +17,7 @@ export default function Nav() {
};
const { data, isLoading, mutate } = swr(
`http://localhost:3050/convos?pageNumber=${pageNumber}`
`http://localhost:3080/api/convos?pageNumber=${pageNumber}`
, onSuccess);
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);

Some files were not shown because too many files have changed in this diff Show more