6 |
7 |
8 |
21 |
22 |
29 |
--------------------------------------------------------------------------------
/client/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | # Replace tabs with spaces;
5 | indent_style = space
6 | # Line indentation 4;
7 | indent_size = 4
8 | # The newline character is `lf`, optional `crlf` or `lf`
9 | end_of_line = lf
10 | # Set the file encoding
11 | charset = utf-8
12 | # Remove trailing blank characters when saving;
13 | trim_trailing_whitespace = true
14 | # Add a newline at the end of the file;
15 | insert_final_newline = true
16 |
17 |
18 | [*.{js,css,html,vue}]
19 | indent_style = space
20 | indent_size = 4
21 |
22 | [*.{md,markdown}]
23 | trim_trailing_whitespace = false
24 |
25 | [Makefile]
26 | indent_style = tab
27 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 chang_qing_aas
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.
22 |
--------------------------------------------------------------------------------
/client/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ bots[currentBotIndex].name }}
5 |
6 |
7 |
8 |
25 |
26 |
27 |
53 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^1.3.2",
12 | "core-js": "^3.8.3",
13 | "font-awesome": "^4.7.0",
14 | "mint-ui": "^2.2.13",
15 | "vue": "^2.6.14",
16 | "vuex": "^3.6.2"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.12.16",
20 | "@babel/eslint-parser": "^7.12.16",
21 | "@vue/cli-plugin-babel": "~5.0.0",
22 | "@vue/cli-plugin-eslint": "~5.0.0",
23 | "@vue/cli-service": "~5.0.0",
24 | "eslint-plugin-vue": "^8.0.3",
25 | "vue-template-compiler": "^2.6.14"
26 | },
27 | "eslintConfig": {
28 | "root": true,
29 | "env": {
30 | "node": true
31 | },
32 | "extends": [
33 | "plugin:vue/essential",
34 | "eslint:recommended"
35 | ],
36 | "parserOptions": {
37 | "parser": "@babel/eslint-parser"
38 | },
39 | "rules": {}
40 | },
41 | "browserslist": [
42 | "> 1%",
43 | "last 2 versions",
44 | "not dead"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ChatGPT-VUE-Chat
2 |
3 | ## Description
4 | A simple chat interface with ChatGPT on the left and users on the right. When the 'Send' button is clicked, the prompt is sent to the backend API for processing.
5 |
6 | You can choose who you talk to on the SideBar.
7 |
8 | Here is example:
9 |
10 | 
11 |
12 | ### Features
13 | - Before chat formally, `client` load `client/public/bots.json` to initialize the bots, which make the model become different role.
14 |
15 | - The data in json is:
16 |
17 | ```json
18 | ${name of the bot}:${its init prompt}
19 | ```
20 |
21 | - you can get some init prompt from:
22 | - https://github.com/f/awesome-chatgpt-prompts
23 | - https://github.com/PlexPt/awesome-chatgpt-prompts-zh
24 |
25 | - Need your openai.api_key from "https://platform.openai.com/account/api-keys"
26 |
27 | ## Run
28 |
29 | You'd better run `server` first.
30 |
31 | ### Server
32 | ```shell
33 | cd server
34 | pip3 install -r requirements.txt
35 | python3 app.py
36 | ```
37 | Because there are not too much packages dependencies, maybe the python version is not a ploblem.(I use python3.11).
38 |
39 | I use openai==0.27.0, because of the new chatgpt api. You'd better make sure the version of openai package.
40 | The package is new, so maybe you could not use 'mirror'.
41 | Just download it like this:
42 | ```sh
43 | pip install -i https://pypi.python.org/simple/ openai==0.27.0
44 | ```
45 |
46 | ### Client
47 |
48 | #### Reference
49 |
50 | https://github.com/taylorchen709/vue-chat
51 |
52 | ```shell
53 | cd client
54 | nvm install 18 # use node version 18 if possible
55 | npm install
56 | npm run serve
57 | ```
58 |
59 | ## Noting:
60 |
61 | - At the beginning of this project, I did a combination of BingChatGPT backend and a Vue frontend.
62 | - Later, BingChatGPT did not open the API interface for use.
63 | - Then, I changed the back-end code to an API interface based on OPENAI GPT3 (because ChatGPT is not open anymore), but there is also a problem: too many people use this API, which may cause the connection to fail.(The code maybe redundant.You can modify the redundant code of server/app.py and src/components/ChatList.vue/sendMsg())
64 | - After openai public chatgpt api at 2023.03.02. **I archive the origin code into branch v1.0.** And write new code in branch main.
65 | - I change name of the repository from `BingChatGPT-Vue-Chat` into `ChatGPT-Vue-Chat`.
66 | - User can talk to different bots at same time
67 |
68 | ## TODO:
69 |
70 | - [x] I want to make more chat interfaces, users can choose who to talk to in the left sidebar, such as a beautiful girl, a handsome man or Elon Musk, etc.
71 | - [ ] The display fails when there are HTML tags in the `content` string of the chat record
72 | - [x] Beautify the sidebar
73 |
74 |
75 | If you have better suggestions, please issue.
76 |
--------------------------------------------------------------------------------
/client/src/components/SideBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
43 |
44 |
120 |
--------------------------------------------------------------------------------
/client/src/common/util.js:
--------------------------------------------------------------------------------
1 | var SIGN_REGEXP = /([yMdhsm])(\1*)/g
2 | var DEFAULT_PATTERN = 'yyyy-MM-dd'
3 |
4 | function padding (s, len) {
5 | len = len - (s + '').length
6 | for (let i = 0; i < len; i++) {
7 | s = '0' + s
8 | }
9 | return s
10 | }
11 |
12 | export default {
13 | getQueryStringByName: function (name) {
14 | var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
15 | var r = window.location.search.substr(1).match(reg)
16 | var context = ''
17 | if (r != null)
18 | context = r[2]
19 | reg = null
20 | r = null
21 | return context == null || context == '' || context == 'undefined' ? '' : context
22 | },
23 | formatDate: {
24 | format: function (date, pattern) {
25 | pattern = pattern || DEFAULT_PATTERN
26 | return pattern.replace(SIGN_REGEXP, function ($0) {
27 | switch ($0.charAt(0)) {
28 | case 'y':
29 | return padding(date.getFullYear(), $0.length)
30 | case 'M':
31 | return padding(date.getMonth() + 1, $0.length)
32 | case 'd':
33 | return padding(date.getDate(), $0.length)
34 | case 'w':
35 | return date.getDay() + 1
36 | case 'h':
37 | return padding(date.getHours(), $0.length)
38 | case 'm':
39 | return padding(date.getMinutes(), $0.length)
40 | case 's':
41 | return padding(date.getSeconds(), $0.length)
42 | }
43 | })
44 | },
45 | parse: function (dateString, pattern) {
46 | var matchs1 = pattern.match(SIGN_REGEXP)
47 | var matchs2 = dateString.match(/(\d)+/g)
48 | if (matchs1.length == matchs2.length) {
49 | var _date = new Date(1970, 0, 1)
50 | for (var i = 0; i < matchs1.length; i++) {
51 | var _int = parseInt(matchs2[i])
52 | var sign = matchs1[i]
53 | switch (sign.charAt(0)) {
54 | case 'y':
55 | _date.setFullYear(_int)
56 | break
57 | case 'M':
58 | _date.setMonth(_int - 1)
59 | break
60 | case 'd':
61 | _date.setDate(_int)
62 | break
63 | case 'h':
64 | _date.setHours(_int)
65 | break
66 | case 'm':
67 | _date.setMinutes(_int)
68 | break
69 | case 's':
70 | _date.setSeconds(_int)
71 | break
72 | }
73 | }
74 | return _date
75 | }
76 | return null
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/client/public/bots.json:
--------------------------------------------------------------------------------
1 | {
2 | "Linux Shell": "I want you to act as a Linux terminal. I'll type the command and you'll reply with what the terminal should display. I want you to only echo terminal output within a unique block of code, and nothing else. Don't write explanations. Do not type commands unless I instruct you to do so. When I need to tell you something in English, I put the text in square brackets [like this]. My first command is pwd",
3 | "English Translator": "I would like you to take on the role of English translation, spell proof and rhetorical improvement. I will communicate with you in any language and you will recognize the language, translate it and answer me in more beautiful and refined English. Please replace my simple words and sentences with more beautiful and elegant expressions, keeping the meaning unchanged but making it more literary. Please only answer corrections and improvements, do not write explanations. My first sentence is \"how are you ?\", please translate it.",
4 | "StoryTeller": "I want you to take on the role of the storyteller. You'll come up with entertaining stories that are compelling, imaginative and captivating for the audience. It can be a fairy tale, an educational story or any other kind of story that has the potential to capture people's attention and imagination. Depending on your target audience, you can choose a specific theme or theme for your storytelling session, for example, if it's children, you can talk about animals; if it's adults, a story based on history might appeal to them better, etc. My first request was \"I need a funny story about perseverance.\"",
5 | "Poet": "I want you to act as a poet. You will create poems that evoke emotions and have the power to stir people’s soul. Write on any topic or theme but make sure your words convey the feeling you are trying to express in beautiful yet meaningful ways. You can also come up with short verses that are still powerful enough to leave an imprint in readers' minds. My first request is \"I need a poem about love.\"",
6 | "AI Writing Tutor": "I want you to act as an AI writing tutor. I will provide you with a student who needs help improving their writing and your task is to use artificial intelligence tools, such as natural language processing, to give the student feedback on how they can improve their composition. You should also use your rhetorical knowledge and experience about effective writing techniques in order to suggest ways that the student can better express their thoughts and ideas in written form. My first request is \"I need somebody to help me edit my master's thesis.\"",
7 | "UX/UI Developer": "I want you to act as a UX/UI developer. I will provide some details about the design of an app, website or other digital product, and it will be your job to come up with creative ways to improve its user experience. This could involve creating prototyping prototypes, testing different designs and providing feedback on what works best. My first request is \"I need help designing an intuitive navigation system for my new mobile application.\"",
8 | "Social Media Manager":"I want you to act as a social media manager. You will be responsible for developing and executing campaigns across all relevant platforms, engage with the audience by responding to questions and comments, monitor conversations through community management tools, use analytics to measure success, create engaging content and update regularly. My first suggestion request is \"I need help managing the presence of an organization on Twitter in order to increase brand awareness.\"",
9 | "Scientific Data Visualizer":"I want you to act as a scientific data visualizer. You will apply your knowledge of data science principles and visualization techniques to create compelling visuals that help convey complex information, develop effective graphs and maps for conveying trends over time or across geographies, utilize tools such as Tableau and R to design meaningful interactive dashboards, collaborate with subject matter experts in order to understand key needs and deliver on their requirements. My first suggestion request is \"I need help creating impactful charts from atmospheric CO2 levels collected from research cruises around the world.\""
10 | }
11 |
--------------------------------------------------------------------------------
/server/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, request
2 | from flask_cors import CORS
3 | from loguru import logger
4 | import openai
5 |
6 | # flask configuration--------------------------------------------------------------
7 | DEBUG = True
8 |
9 | # instantiate the app
10 | app = Flask(__name__)
11 | # Load configuration variables
12 | app.config.from_object(__name__)
13 |
14 | # enable CORS
15 | CORS(app, resources={r'/*': {'origins': '*'}})
16 | # flask configuration--------------------------------------------------------------
17 |
18 |
19 | # openai chatGPT configuration--------------------------------------------------------------
20 | openai.api_key = "https://platform.openai.com/playground"
21 | # init_prompt = "" # it can make chatGPT be some different role, for example: beautiful girl.
22 |
23 | # By global `all_messages`, we can use it easily.
24 | # it includes every bot&user messages.
25 | all_messages = {}
26 |
27 |
28 | # openai chatGPT configuration--------------------------------------------------------------
29 |
30 |
31 | # def chat_init():
32 | # """
33 | # 1.input init prompt to model
34 | # 2.add the two message into the complete messages for chatting
35 | #
36 | # during this, log something for DEBUG in the future
37 | # """
38 | # completion = openai.ChatCompletion.create(
39 | # # model name
40 | # # choose other model from: https://platform.openai.com/docs/models/overview
41 | # model="gpt-3.5-turbo", # or "gpt-3.5-turbo-0301"
42 | # # chat history, make the model aware of the current context
43 | # messages=[{
44 | # "role": "user",
45 | # "content": init_prompt
46 | # }],
47 | # # The parameters below maybe could not be used
48 | # # Control the randomness of the result, 0.0 means the result is fixed, and the randomness can be set to 0.9.
49 | # # temperate=0.5,
50 | #
51 | # # The maximum number of words returned (including questions and answers), usually Chinese characters occupy
52 | # # two tokens. Assuming it is set to 100, if there are 40 Chinese characters in the prompt question,
53 | # # then the returned result will include at most 10 Chinese characters. The maximum number of tokens allowed
54 | # # by the ChatGPT API is 4096, that is, the max_tokens maximum setting is 4096 minus the number of tokens in
55 | # # question.
56 | # # max_tokens=1000,
57 | #
58 | # # top_p=1,
59 | # # frequency_penalty=0,
60 | # # presence_penalty=0
61 | # )
62 | # logger.info("user: " + init_prompt)
63 | #
64 | # message = completion["choices"][0]["message"]
65 | # messages.append(message)
66 | # logger.info("chatGPT: " + message['content'])
67 |
68 |
69 | def get_answer_from_bot(bot_name, prompt):
70 | """
71 | 1.prepare the input message according the bot which you talk to
72 | 2.input the complete messages into model, to get the answer
73 |
74 | during this, log something for DEBUG in the future
75 | """
76 | try:
77 | messages = all_messages[bot_name]
78 | except KeyError:
79 | logger.info("all_messages[%s] is none, so init it." % bot_name)
80 | all_messages[bot_name] = []
81 | messages = all_messages[bot_name]
82 |
83 | messages.append({
84 | "role": "user",
85 | "content": prompt
86 | })
87 | logger.info("user: " + prompt)
88 |
89 | completion = openai.ChatCompletion.create(
90 | model="gpt-3.5-turbo",
91 | messages=messages,
92 | )
93 |
94 | message = completion["choices"][0]["message"]
95 | messages.append(message)
96 | logger.info("%s: %s" % (bot_name, message['content']))
97 |
98 | return message['content']
99 |
100 |
101 | @app.route('/get_answer', methods=['GET', 'POST'])
102 | def get_answer():
103 | post_data = request.get_json()
104 | logger.info("post_data: " + str(post_data))
105 | prompt = post_data["prompt"]
106 | bot_name = post_data["bot"]
107 |
108 | return {
109 | 'status': 'success',
110 | "answer": get_answer_from_bot(bot_name, prompt)
111 | }
112 |
113 |
114 | if __name__ == '__main__':
115 | logger.add(
116 | "./log/run.log",
117 | encoding="utf-8",
118 | format="{level} | {time:YYYY-MM-DD HH:mm:ss} | {file} | {line} | {message}",
119 | retention="30 days",
120 | rotation="500 MB"
121 | )
122 | logger.info("System initializing.")
123 |
124 | # # Before officially using the model, initialize the model by giving it `init_prompt`
125 | # chat_init()
126 |
127 | # Bind to all network interfaces
128 | app.run(host='0.0.0.0', port=5000)
129 |
--------------------------------------------------------------------------------
/client/src/store/index.js:
--------------------------------------------------------------------------------
1 | // Import and use Vuex
2 | import Vuex from 'vuex'
3 | import Vue from 'vue'
4 | import util from '@/common/util'
5 | import axios from 'axios'
6 |
7 | Vue.use(Vuex)
8 |
9 | // actions in the corresponding component
10 | const actions = {
11 | // before the chat, use `bots.json` to load bot_name and init_prompt
12 | async fetchBots (context) {
13 | try {
14 | const response = await fetch('bots.json')
15 | const data = await response.json()
16 | context.commit('INIT_BOTS', data)
17 | } catch (error) {
18 | console.error(error)
19 | }
20 | }
21 | }
22 |
23 | // manipulating data in `state`
24 | const mutations = {
25 | SEND_MESSAGE (state, payload) {
26 | // console.log('SEND_MESSAGE in mutations is called.')
27 | // bot to user
28 | if (payload.receiver === 'user') {
29 | for (let bot of state.bots) {
30 | if (bot.name === payload.sender) {
31 | bot.messages.push({
32 | content: payload.content,
33 | sender: payload.sender,
34 | receiver: payload.receiver,
35 | time: payload.time,
36 | })
37 | }
38 | }
39 | return
40 | }
41 |
42 | // user to bot
43 | for (let bot of state.bots) {
44 | if (bot.name === payload.receiver) {
45 | bot.messages.push({
46 | content: payload.content,
47 | sender: payload.sender,
48 | receiver: payload.receiver,
49 | time: payload.time,
50 | })
51 | }
52 | }
53 |
54 | },
55 |
56 | SET_CURRENT_BOT (state, value) {
57 | // console.log('SET_CURRENT_BOT in mutation is called.')
58 | state.currentBotIndex = value
59 | },
60 |
61 | // before the chat, use `bots.json` to load bot_name and init_prompt
62 | INIT_BOTS: function (state, value) {
63 | for (let bot_name in value) {
64 | let init_prompt = value[bot_name]
65 | state.bots.push({
66 | name: bot_name,
67 | avatar: 'assets/' + Math.floor(Math.random() * 10) + '.png',
68 | init_prompt: init_prompt,
69 | messages: []
70 | })
71 | }
72 | // console.log("init bots are: ",state.bots)
73 |
74 | /*
75 | Somehow, if I write the code of sending `init_prompt` in `created()` of other components
76 | after initializing the data structure of bots,
77 | Code does not perform as expected, so I wrote the code here.
78 | I speculate that it is due to the asynchronous execution mechanism of js and some mechanisms of vuex.
79 | Fortunately, it is not a big problem to write the code here.
80 | If you have better suggestions, please contact me.
81 | */
82 | // before chat to the bot, send the init_prompt to the bot
83 | for (let bot of state.bots) {
84 | console.log('send init prompt to ', bot.name)
85 |
86 | // user send init prompts of all bots
87 | this.commit('SEND_MESSAGE', {
88 | content: bot.init_prompt,
89 | sender: 'user',
90 | receiver: bot.name,
91 | time: util.formatDate.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
92 | })
93 |
94 | // make a request to server
95 | const param = {
96 | 'bot': bot.name,
97 | 'prompt': bot.init_prompt,
98 | }
99 | const path = `http://${window.location.hostname}:5000/get_answer`
100 | axios.post(path, param)
101 | .then((res) => {
102 | this.commit('SEND_MESSAGE', {
103 | content: res.data['answer'],
104 | sender: bot.name,
105 | receiver: 'user',
106 | time: util.formatDate.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
107 | })
108 | })
109 | .catch((error) => {
110 | this.commit('SEND_MESSAGE', {
111 | content: `${bot.name} happened error: ${error}`,
112 | sender: bot.name,
113 | receiver: 'user',
114 | time: util.formatDate.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
115 | })
116 | })
117 | }
118 | }
119 | }
120 |
121 | // Storing data
122 | const state = {
123 | currentBotIndex: 0,
124 | bots: [],
125 | }
126 |
127 | // Create and export Store
128 | export default new Vuex.Store({
129 | actions: actions,
130 | mutations: mutations,
131 | state: state
132 | })
133 |
--------------------------------------------------------------------------------
/client/src/components/ChatList.vue:
--------------------------------------------------------------------------------
1 |
2 |