├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── README_zh.md
├── chatbot
├── __init__.py
├── asgi.py
├── config.py
├── settings.py
├── urls.py
└── wsgi.py
├── chatchatchat
├── __init__.py
├── admin.py
├── apps.py
├── demo.html
├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ └── createsecretkey.py
├── migrations
│ └── __init__.py
├── models.py
├── static
│ ├── css
│ │ └── style.css
│ ├── images
│ │ └── default-head.png
│ ├── js
│ │ ├── character.js
│ │ ├── chat.js
│ │ ├── edit.js
│ │ ├── helpers.js
│ │ ├── initialize.js
│ │ ├── memory.js
│ │ ├── messageHistory.js
│ │ ├── roleplay.js
│ │ └── script.js
│ └── media
│ │ └── start-speech.mp3
├── templates
│ └── index.html
├── tests.py
├── urls.py
└── views.py
├── image
└── README
│ ├── 1681363127495.png
│ ├── 1681363147175.png
│ ├── 1681363512437.png
│ └── 1681363557274.png
├── manage.py
├── requirements.txt
└── updatedemo.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | *.sqlite3
4 | *.pyc
5 | chatbot/mine_config.py
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 lilycyf
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chatbot RPG using OpenAI's gpt models
2 |
3 | [中文](https://github.com/lilycyf/chatgpt-rpg/blob/master/README_zh.md) | English
4 |
5 | The objective of this project is to utilize OpenAI's API to create a chatbot that possesses unique characteristics and integrate it into an RPG game.
6 |
7 | Currently, the project features two built characters, Li Ming and Avery Kim, whom you can interact with under the roleplay section of the demo. To participate, make sure to obtain your own OpenAI KPI and input it in the provided field at the bottom left. It's essential to note that the demo is a silent website without a backend, so your OpenAI API won't be transmitted to anyone. In addition to the two characters, you can also communicate with the basic chatgpt via the chat section.
8 |
9 | Note: You can sign up for an API key from OpenAI. Generate an API key for OpenAI's service by following the instructions on the [OpenAI website](https://platform.openai.com/account/api-keys) for free.
10 |
11 | 🔗 [demo](https://lilycyf.github.io/chatgpt-rpg/chatchatchat/demo.html)
12 |
13 | ## Screenshots
14 |
15 |
16 |
17 | 
18 |
19 | ## Getting Started
20 |
21 | 1. Clone this repository to your local machine.
22 | 2. (optional) Create a virtual environment:
23 | 1. Run the command `python3 -m venv venv` to create a new virtual environment named "venv".
24 | 2. Activate the virtual environment by running the command `source venv/bin/activate`. On Windows, use `.\venv\Scripts\activate` instead.
25 | 3. Install the required packages listed in the `requirements.txt` file using `pip install -r requirements.txt` in your terminal.
26 | 4. Make a copy of `config.py` under the `chatbot` folder with name `mine_config.py`.
27 | 5. Generate your own secret key in Django by running the command `python manage.py generate_secret_key` in your terminal. Replace the `SECRET_KEY` in `mine_config.py` under the `chatbot` folder with the key that you generated.
28 | 6. Sign up for an API key from OpenAI. Generate an API key for OpenAI's service by following the instructions on the [OpenAI website](https://platform.openai.com/account/api-keys) and replace the `OPENAI_API_KEY` in `mine_config.py` under the `chatbot` folder with your own key.
29 |
30 | ## Running the Chatbot
31 |
32 | Note: If you set up a virtual environment in step 2 you will need to activate it before running any commands related to the project. To activate the virtual environment, run `source env/bin/activate`. To deactivate it, simply run the `deactivate` command.
33 |
34 | Before start the server, navigate to the root folder of the project in your terminal and run the command `python manage.py migrate` to apply the database migrations.
35 |
36 | To run the chatbot, run the command `python manage.py runserver`. Then open your web browser and go to `http://localhost:8000` to access the chatbot application.
37 |
38 | ## Conclusion
39 |
40 | This project provides an example of how to build a chatbot application using OpenAI's gpt-3.5-turbo. Feel free to modify the code to suit your needs, and don't forget to create your own secret key and API key when running the application.
41 |
42 | ## License
43 |
44 | This project is released under the [MIT License](./LICENSE).
45 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | # Chatbot RPG using OpenAI's gpt models
2 |
3 | 中文 | [English](https://github.com/lilycyf/chatgpt-rpg/blob/master/README.md)
4 |
5 | 这个项目的目标是利用OpenAI的API创建一个具有独特特征并将其整合到RPG游戏中的聊天机器人。
6 |
7 | 目前,该项目包括两个构建好的角色,李明和艾弗里·金,你可以在demo的role play与他们互动。为了参与其中,请确保获取自己的OpenAI KPI,并在左下方的输入框中提交。demo是一个没有后端的静态网站,因此你的OpenAI API不会被传输给任何人。除了两个角色之外,你还可以通过chat与基本的ChatGPT进行交流。
8 |
9 | 注意:你可以按照[OpenAI网站](https://platform.openai.com/account/api-keys)上的说明免费生成OpenAI服务的API密钥。
10 |
11 | 🔗 [demo](https://lilycyf.github.io/chatgpt-rpg/chatchatchat/demo.html)
12 |
13 | ## Screenshots
14 |
15 |
16 |
17 | 
18 |
19 | ## Getting Started
20 |
21 | 1. 将此存储库克隆到本地计算机。
22 | 2. (可选)创建虚拟环境:
23 | 1. 运行命令 `python3 -m venv venv`以创建名为 "venv" 的新虚拟环境。
24 | 2. 激活虚拟环境,请在终端中运行命令 `source venv/bin/activate`。在 Windows 上,请使用 `.\venv\Scripts\activate`。
25 | 3. 在终端中使用 `pip install -r requirements.txt` 命令安装 `requirements.txt` 文件中列出的必需包。
26 | 4. 复制 `chatbot` 文件夹下的 `config.py` 文件并将其重命名为 `mine_config.py`。
27 | 5. 在终端中运行命令 `python manage.py generate_secret_key` 以在 Django 中生成自己的密钥。然后将 `chatbot` 文件夹下 `mine_config.py` 文件中的 `SECRET_KEY` 替换为你生成的密钥。
28 | 6. 在 OpenAI 上注册 API 密钥。请按照 [OpenAI 网站](https://platform.openai.com/account/api-keys) 上的说明生成 OpenAI 服务的 API 密钥,然后将 `chatbot` 文件夹下的 `mine_config.py` 文件中的 `OPENAI_API_KEY` 替换为你自己的密钥。
29 |
30 | ## Running the Chatbot
31 |
32 | 注意:如果在步骤2中设置了虚拟环境,则在运行与该项目相关的任何命令之前,你需要激活它。要激活虚拟环境,请运行 `source env/bin/activate`。要停用它,只需运行 `deactivate`命令。
33 |
34 | 在开始服务器之前,请在终端中导航到项目的根文件夹,并运行命令 `python manage.py migrate`以应用数据库迁移。
35 |
36 | 要运行聊天机器人,请运行命令 `python manage.py runserver`。然后打开你的Web浏览器,转到 `http://localhost:8000`以访问聊天机器人应用程序。
37 |
38 | ## Conclusion
39 |
40 | 该项目提供了如何使用OpenAI的gpt-3.5-turbo构建聊天机器人应用程序的示例。随意修改代码以满足你的需求,并在运行应用程序时不要忘记创建自己的秘密密钥和API密钥。
41 |
42 | ## License
43 |
44 | This project is released under the [MIT License](./LICENSE).
45 |
--------------------------------------------------------------------------------
/chatbot/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatbot/__init__.py
--------------------------------------------------------------------------------
/chatbot/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for chatbot project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chatbot.settings")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/chatbot/config.py:
--------------------------------------------------------------------------------
1 | SECRET_KEY = ''
2 | OPENAI_API_KEY = ''
3 |
--------------------------------------------------------------------------------
/chatbot/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for chatbot project.
3 |
4 | Generated by 'django-admin startproject' using Django 4.1.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/4.1/ref/settings/
11 | """
12 | from .mine_config import SECRET_KEY, OPENAI_API_KEY
13 | from pathlib import Path
14 |
15 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
16 | BASE_DIR = Path(__file__).resolve().parent.parent
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = SECRET_KEY
24 | OPENAI_API_KEY = OPENAI_API_KEY
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = True
28 |
29 | ALLOWED_HOSTS = []
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | "django.contrib.admin",
35 | "django.contrib.auth",
36 | "django.contrib.contenttypes",
37 | "django.contrib.sessions",
38 | "django.contrib.messages",
39 | "django.contrib.staticfiles",
40 | "chatchatchat"
41 | ]
42 |
43 | MIDDLEWARE = [
44 | "django.middleware.security.SecurityMiddleware",
45 | "django.contrib.sessions.middleware.SessionMiddleware",
46 | "django.middleware.common.CommonMiddleware",
47 | "django.middleware.csrf.CsrfViewMiddleware",
48 | "django.contrib.auth.middleware.AuthenticationMiddleware",
49 | "django.contrib.messages.middleware.MessageMiddleware",
50 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
51 | ]
52 |
53 | ROOT_URLCONF = "chatbot.urls"
54 |
55 | TEMPLATES = [
56 | {
57 | "BACKEND": "django.template.backends.django.DjangoTemplates",
58 | "DIRS": [],
59 | "APP_DIRS": True,
60 | "OPTIONS": {
61 | "context_processors": [
62 | "django.template.context_processors.debug",
63 | "django.template.context_processors.request",
64 | "django.contrib.auth.context_processors.auth",
65 | "django.contrib.messages.context_processors.messages",
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = "chatbot.wsgi.application"
72 |
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases
76 |
77 | DATABASES = {
78 | "default": {
79 | "ENGINE": "django.db.backends.sqlite3",
80 | "NAME": BASE_DIR / "db.sqlite3",
81 | }
82 | }
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
91 | },
92 | {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
93 | {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
94 | {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
95 | ]
96 |
97 |
98 | # Internationalization
99 | # https://docs.djangoproject.com/en/4.1/topics/i18n/
100 |
101 | LANGUAGE_CODE = "en-us"
102 |
103 | TIME_ZONE = "UTC"
104 |
105 | USE_I18N = True
106 |
107 | USE_TZ = True
108 |
109 |
110 | # Static files (CSS, JavaScript, Images)
111 | # https://docs.djangoproject.com/en/4.1/howto/static-files/
112 |
113 | STATIC_URL = "static/"
114 |
115 | # Default primary key field type
116 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
117 |
118 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
119 |
--------------------------------------------------------------------------------
/chatbot/urls.py:
--------------------------------------------------------------------------------
1 | """chatbot URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import include, path
18 |
19 | urlpatterns = [
20 | path('', include('chatchatchat.urls')),
21 | ]
22 |
23 |
24 |
--------------------------------------------------------------------------------
/chatbot/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for chatbot project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chatbot.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/chatchatchat/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatchatchat/__init__.py
--------------------------------------------------------------------------------
/chatchatchat/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/chatchatchat/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ChatchatchatConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "chatchatchat"
7 |
--------------------------------------------------------------------------------
/chatchatchat/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Unleash Your Creativity: A Guide to Character Creation
183 |
🎉 Welcome to the exciting world of character creation, where your imagination has no limits! 🌟 With each question answered, you can design unique and compelling characters, giving them depth and authenticity. 🧑🎨 You can role-play with two predefined characters in the role-play section or use the chat section to interact with an AI assistant. 💬 🤔 Please be aware that some questions may be sensitive when role-playing, such as those regarding the current weather or the character's physical state. 🌧️ If the character responds that they lack a physical body or access to real-time weather updates, instruct them to reply as if they were not an AI language model. If that doesn't work, restart the conversation to avoid misunderstandings. 🔁 💭 Remember, this is a safe and fun space for creativity, so let your imagination run wild and bring your dream character to life! 🦄🎨
184 |
185 |
1133 |
1134 |
1135 | ×
1136 |
Warning!!
1137 |
Once you submit, you can no longer change your answers.
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 |
1146 |
1147 |
1148 |
1149 |
1150 |
1151 |
1153 |
Li Ming
1154 |
a university student studying business management
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
Hello, my name is Li Ming. I am 21 years old and come from Beijing, China. I am a university student studying business management at the University of Southern California in Los Angeles. My family has always placed a great emphasis on education and encouraged me to pursue academic goals. In my free time, I enjoy playing the piano, reading novels, and exploring new restaurants. I consider myself to be ambitious, organized, and friendly. Studying in the United States has been a challenging but rewarding experience for me. After graduation, I plan to return to China and join my family's real estate development company, hoping to use my education and international experience to contribute to the company's growth and expansion into new markets.
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
Avery Kim
1176 |
a graphic designer based in New York City
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
Hi, my name is Avery Kim. I am a graphic designer based in New York City. I am a creative and curious person who values empathy, independence, and perseverance. As someone who grew up in a collectivist culture, I believe in the importance of group harmony and treating others with kindness and respect. In my free time, I enjoy painting, drawing, and exploring different neighborhoods in the city. I am also passionate about advocating for better mental health services for survivors of crime, having overcome my own struggles with PTSD. I am excited to continue growing my business and working on projects that have a positive impact on society.
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 |
1196 |
1197 |
1198 |
Edit
1199 |
Your friendly AI editbot
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
Hello! How can I assist you today?
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 |
1217 |
1218 |
1219 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
--------------------------------------------------------------------------------
/chatchatchat/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatchatchat/management/__init__.py
--------------------------------------------------------------------------------
/chatchatchat/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatchatchat/management/commands/__init__.py
--------------------------------------------------------------------------------
/chatchatchat/management/commands/createsecretkey.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand
2 | from django.core.management.utils import get_random_secret_key
3 |
4 | class Command(BaseCommand):
5 | help = 'Generates a new secret key'
6 |
7 | def handle(self, *args, **options):
8 | self.stdout.write(get_random_secret_key())
9 |
--------------------------------------------------------------------------------
/chatchatchat/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatchatchat/migrations/__init__.py
--------------------------------------------------------------------------------
/chatchatchat/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/chatchatchat/static/css/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --theme-light:#ffeded;
3 | --theme: #f8cfcf;
4 | /* --theme: linear-gradient(45deg, #ffcda5, #ee4d5f); */
5 | --theme-hover: #edadad;
6 | --button-submit: #e78c8c;
7 | --button-submit-hover: #e37d7d;
8 | }
9 |
10 | html,
11 | body {
12 | height: 100%;
13 | width: 100%;
14 | margin: 0;
15 | padding: 0;
16 | display: flex;
17 | position: fixed;
18 | }
19 | audio {
20 | width: 100%;
21 | }
22 | button {
23 | cursor: pointer;
24 | }
25 |
26 | h1 {
27 | font-size: 2em;
28 | }
29 |
30 | h2,
31 | h3 {
32 | font-size: 2em;
33 | margin: 0px;
34 | margin-bottom: 20px;
35 | }
36 |
37 | .time {
38 | text-align: center;
39 | }
40 |
41 | .freeze-background {
42 | display: none;
43 | /* Hide the warning message box by default */
44 | position: fixed;
45 | /* Stay in place */
46 | z-index: 1;
47 | /* Sit on top */
48 | left: 0;
49 | top: 0;
50 | width: 100%;
51 | /* Full width */
52 | height: 100%;
53 | /* Full height */
54 | overflow: auto;
55 | /* Enable scroll if needed */
56 | background-color: rgba(0, 0, 0, 0.4);
57 | /* Black w/ opacity */
58 | }
59 |
60 | .frozen {
61 | pointer-events: none;
62 | }
63 |
64 | /* top-bar */
65 | .top-bar {
66 | position: fixed;
67 | top: 0;
68 | left: 0;
69 | width: 100%;
70 | height: 40px;
71 | background: var(--theme);
72 | align-items: center;
73 | display: none;
74 | }
75 |
76 | /* guide-bar */
77 | #sidebarToggle {
78 | position: absolute;
79 | display: grid;
80 | height: 40px;
81 | align-items: center;
82 | left: 100%;
83 | top: 0;
84 | }
85 |
86 | .slide-out {
87 | animation-duration: 400ms;
88 | animation-name: slide-out;
89 | }
90 |
91 | @keyframes slide-out {
92 | 0% {
93 | transform: translateX(0%);
94 | opacity: 1;
95 | }
96 |
97 | 100% {
98 | transform: translateX(-100%);
99 | opacity: 0;
100 | }
101 | }
102 |
103 | .slide-in {
104 | animation-duration: 400ms;
105 | animation-name: slide-in;
106 | }
107 |
108 | @keyframes slide-in {
109 | 0% {
110 | transform: translateX(-100%);
111 | opacity: 0;
112 | }
113 |
114 | 100% {
115 | transform: translateX(0%);
116 | opacity: 1;
117 | }
118 | }
119 |
120 | .guide-bar {
121 | top: 0;
122 | left: 0;
123 | width: 200px;
124 | height: 100%;
125 | background: var(--theme);
126 | z-index: 1;
127 | flex-direction: column;
128 | position: relative
129 | }
130 |
131 | .sidebar--collapsed {
132 | width: 0;
133 | }
134 |
135 | .guide-bar-page-buttons {
136 | top: 0;
137 | left: 0;
138 | height: 40px;
139 | background-color: rgb(43, 43, 43);
140 | width: 100%;
141 | display: flex;
142 | flex-wrap: nowrap;
143 | overflow-x: scroll;
144 | }
145 |
146 | .guide-bar-page-buttons::-webkit-scrollbar {
147 | display: none;
148 | }
149 |
150 |
151 | .guide-bar-button {
152 | flex-shrink: 0;
153 | cursor: pointer;
154 | width: 75px;
155 | font-size: 13px;
156 | margin: 0px;
157 | border: none;
158 | background-color: rgb(154, 154, 154);
159 | border-top-right-radius: 10px;
160 | border-top-left-radius: 10px;
161 | box-shadow: inset 0px -2px 10px -5px black;
162 | color: #000;
163 | }
164 |
165 | .guide-bar-button.active {
166 | background: var(--theme);
167 | box-shadow: none;
168 | }
169 |
170 | .apis {
171 | position: absolute;
172 | bottom: 20px;
173 | padding: 5px;
174 | right: 0px;
175 | left: 0px
176 | }
177 |
178 | .api-input {
179 | font-size: 11px;
180 | height: 16px;
181 | padding: 0px;
182 | border: 0px;
183 | width: 100%;
184 | }
185 |
186 | .material-symbols-outlined#open-ai-api-toggle-btn {
187 | font-size: 20px;
188 | margin: 5px;
189 | font-variation-settings: 'OPSZ' 20;
190 | cursor: pointer;
191 | }
192 |
193 | .guide-bar-page-histories {
194 | height: 650px;
195 | overflow-y: scroll;
196 | }
197 |
198 | .guide-bar-page-histories::-webkit-scrollbar {
199 | display: none;
200 | }
201 |
202 | .chat-history {
203 | display: flex;
204 | align-items: center;
205 | height: 40px;
206 | padding: 7px;
207 | cursor: pointer;
208 | border-radius: 15px;
209 | margin: 5px;
210 | }
211 |
212 | /*
213 | .chat-history.active {
214 | background-color: white;
215 | border-top-right-radius: 0px;
216 | border-bottom-right-radius: 0px;
217 | cursor: default;
218 | margin-right: 0px;
219 | } */
220 |
221 | .addnew {
222 | border-width: 1px;
223 | border-color: var(--theme-hover);
224 | border-style: dotted;
225 | position: relative;
226 | }
227 |
228 | .addnew .addnew_sign {
229 | color: black;
230 | position: absolute;
231 | /* top: 50%; */
232 | /* left: 30px; */
233 | /* transform: translate(-50%,-50%); */
234 | }
235 |
236 | .addnew.active {
237 | border-right-width: 0px;
238 | }
239 |
240 | .chat-history-headshot {
241 | height: 100%;
242 | }
243 |
244 | .chat-history-headshot img {
245 | height: 100%;
246 | object-fit: cover;
247 | border-radius: 10px;
248 | }
249 |
250 | .chat-history-name {
251 | flex-grow: 1;
252 | max-height: 100%;
253 | overflow: hidden;
254 | white-space: nowrap;
255 | text-overflow: ellipsis;
256 | padding-left: 10px;
257 | }
258 |
259 | .chat-history-name h4 {
260 |
261 | margin: 0;
262 | }
263 |
264 | .chat-history-name p4 {
265 | margin: 0;
266 | font-size: 14px;
267 | }
268 |
269 | /* chat */
270 |
271 | .container-container,
272 | .chat-container,
273 | .roleplay-container,
274 | .edit-container {
275 | position: relative;
276 | --my-margin-left: 200px;
277 | /* Set the margin value using a CSS variable */
278 | --my-margin-top: 0px;
279 | /* Set the margin value using a CSS variable */
280 | /* margin-left: var(--my-margin-left); */
281 | margin-top: var(--my-margin-top);
282 | flex-grow: 1;
283 | height: calc(100% - var(--my-margin-top));
284 | /* width: calc(100% - var(--my-margin-left)); */
285 | }
286 |
287 | .chat,
288 | .edit,
289 | .roleplay {
290 | height: 100%;
291 | width: 100%;
292 | display: flex;
293 | flex-direction: column;
294 | align-items: center;
295 | justify-content: center;
296 | box-sizing: border-box;
297 | font-family: Arial, sans-serif;
298 | }
299 |
300 | .chat-header,
301 | .edit-header,
302 | .roleplay-header {
303 | /* display: none; */
304 | text-align: center;
305 | margin-bottom: 20px;
306 | }
307 |
308 | .chat-messages,
309 | .edit-messages {
310 | height: 100%;
311 | width: 70%;
312 | margin-left: 15%;
313 | margin-right: 15%;
314 | margin-bottom: 50px;
315 | overflow-y: scroll;
316 | display: flex;
317 | flex-direction: column;
318 | }
319 |
320 | .chat-messages::-webkit-scrollbar,
321 | .edit-messages::-webkit-scrollbar {
322 | display: none;
323 | }
324 |
325 | .chatbot-message-container {
326 | display: flex;
327 | flex-direction: row;
328 | }
329 |
330 | .user-message-container {
331 | display: flex;
332 | flex-direction: row-reverse;
333 | }
334 |
335 | .user-message-container .message-headshot,
336 | .chatbot-message-container .message-headshot {
337 | height: 40px;
338 | width: 40px;
339 | margin: 10px 0px;
340 | position: relative;
341 | }
342 |
343 | .user-message-container .message-headshot img,
344 | .chatbot-message-container .message-headshot img {
345 | height: 100%;
346 | object-fit: cover;
347 | border-radius: 10px;
348 | }
349 |
350 | .chatbot-message {
351 | padding: 20px;
352 | max-width: 75%;
353 | align-self: flex-start;
354 | background: var(--theme);
355 | /* border: rgb(203, 203, 203); */
356 | border-radius: 10px;
357 | border-top-left-radius: 0px;
358 | /* border-width: 0px;
359 | border-top-width: 0.5px; */
360 | border-style: solid;
361 | margin: 10px;
362 | border-width: 1px;
363 | }
364 |
365 | .user-message {
366 | padding: 20px;
367 | max-width: 80%;
368 | align-self: flex-end;
369 | background: var(--theme);
370 | /* border: rgb(203, 203, 203); */
371 | border-radius: 10px;
372 | border-top-right-radius: 0px;
373 | /* border-width: 0px;
374 | border-top-width: 0.5px; */
375 | border-style: solid;
376 | margin: 10px;
377 | border-width: 1px;
378 | }
379 |
380 | .start-message {
381 | margin-bottom: 10px;
382 | }
383 |
384 | .end-message {
385 | margin-bottom: 120px;
386 | }
387 |
388 | .chatbot-input {
389 | display: flex;
390 | position: absolute;
391 | bottom: 50px;
392 | left: 50%;
393 | transform: translateX(-50%);
394 | width: 70%;
395 | background: var(--theme);
396 | padding: 10px;
397 | border-radius: 10px;
398 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
399 | height: auto;
400 | }
401 |
402 | .chatbot-input textarea {
403 | font-size: medium;
404 | flex-grow: 1;
405 | padding: 10px;
406 | border-radius: 5px;
407 | border: none;
408 | margin-right: 10px;
409 | resize: none;
410 | max-height: 200px;
411 | margin-top: 0px;
412 | margin-bottom: 0px;
413 | }
414 |
415 | .chatbot-input button {
416 | background-color: var(--button-submit);
417 | color: white;
418 | border: none;
419 | border-radius: 5px;
420 | padding: 10px 20px;
421 | cursor: pointer;
422 | }
423 |
424 |
425 |
426 | /* roleplay */
427 |
428 |
429 | /* roleplay-survey */
430 |
431 | .roleplaybot-personality {
432 | display: flex;
433 | position: absolute;
434 | top: 10px;
435 | right: 10px;
436 | background: var(--theme);
437 | padding: 0px;
438 | border-radius: 25px;
439 | border-width: 1px;
440 | border-style: solid;
441 | border-color: var(--theme-hover);
442 | }
443 |
444 | .roleplaybot-personality button {
445 | background: var(--theme);
446 | color: #000;
447 | border: none;
448 | border-radius: 25px;
449 | padding: 0px;
450 | cursor: pointer;
451 | width: 40px;
452 | height: 40px;
453 | }
454 |
455 | .roleplaybot-personality-survey {
456 | position: absolute;
457 | top: 0px;
458 | right: 0px;
459 | width: 100%;
460 | height: 100%;
461 | background: white;
462 | overflow-y: scroll;
463 | }
464 |
465 | .roleplaybot-personality-content {
466 | background-color: var(--theme-light);
467 | border: none;
468 | border-radius: 5px;
469 | padding: 10px;
470 | margin: 20px;
471 | }
472 |
473 | body {
474 | font-family: Arial, sans-serif;
475 | color: #333;
476 | }
477 |
478 | form {
479 | margin: 0 auto;
480 | }
481 |
482 | label {
483 | margin-bottom: 0.5em;
484 | }
485 |
486 | .choices {
487 | margin-bottom: 1em;
488 | }
489 |
490 | input[type="text"],
491 | .other-text,
492 | textarea {
493 | padding: 0.5em;
494 | border: 1px solid #ccc;
495 | border-radius: 0.25em;
496 | font-size: 1em;
497 | font-family: Arial, sans-serif;
498 | }
499 |
500 | input[type="radio"],
501 | input[type="checkbox"] {
502 | margin-right: 0.5em;
503 | }
504 |
505 | button[type="submit"] {
506 | padding: 0.5em 1em;
507 | background-color: var(--button-submit);
508 | color: #fff;
509 | border: none;
510 | border-radius: 0.25em;
511 | cursor: pointer;
512 | width: 100%;
513 | margin: 0px;
514 | }
515 |
516 |
517 |
518 | button[type="submit"]:disabled {
519 | background-color: #c9c9c9;
520 | cursor: not-allowed;
521 | }
522 |
523 | .long-answer {
524 | display: flex;
525 | flex-direction: column;
526 | }
527 |
528 | /* roleplay-survey warning message */
529 |
530 | /* The warning message box */
531 | #warning-modal {
532 | display: none;
533 | /* Hide the warning message box by default */
534 | position: fixed;
535 | /* Stay in place */
536 | z-index: 1;
537 | /* Sit on top */
538 | left: 0;
539 | top: 0;
540 | width: 100%;
541 | /* Full width */
542 | height: 100%;
543 | /* Full height */
544 | overflow: auto;
545 | /* Enable scroll if needed */
546 | background-color: rgba(0, 0, 0, 0.4);
547 | /* Black w/ opacity */
548 | }
549 |
550 | /* Warning message box content */
551 | .warning-modal-content {
552 | background-color: #ffffff;
553 | margin: 15% auto;
554 | padding: 20px;
555 | border: 1px solid #888;
556 | width: 60%;
557 | text-align: center;
558 | }
559 |
560 | /* Warning message box title */
561 | .warning-modal-title,
562 | .warning-modal-text {
563 | text-align: left;
564 | }
565 |
566 | /* Warning message box close button */
567 | .warning-modal-close {
568 | color: #aaa;
569 | float: right;
570 | font-size: 28px;
571 | font-weight: bold;
572 | text-align: center;
573 | width: 30px;
574 | height: 30px;
575 | margin-top: -20px;
576 | margin-right: -20px;
577 | }
578 |
579 |
580 | /* Warning message box buttons */
581 | .warning-modal-buttons {
582 | display: flex;
583 | justify-content: space-between;
584 | margin-top: 20px;
585 | }
586 |
587 | .warning-modal-buttons button {
588 | padding: 10px 0px;
589 | border: none;
590 | border-radius: 5px;
591 | font-size: 16px;
592 | cursor: pointer;
593 | width: 48%;
594 | }
595 |
596 | .warning-modal-buttons button:focus {
597 | outline: none;
598 | }
599 |
600 | .warning-modal-buttons #warning-cancel-btn {
601 | background-color: #ccc;
602 | color: #000;
603 | }
604 |
605 | .warning-modal-buttons #warning-submit-btn {
606 | background-color: var(--button-submit);
607 | color: #fff;
608 | }
609 |
610 |
611 | /* edit */
612 |
613 | .edit-input {
614 | display: flex;
615 | position: absolute;
616 | bottom: 50px;
617 | left: 50%;
618 | transform: translateX(-50%);
619 | width: 70%;
620 | background: var(--theme);
621 | padding: 10px;
622 | border-radius: 10px;
623 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
624 | height: auto;
625 | }
626 |
627 | .edit-input textarea {
628 | font-size: medium;
629 | flex-grow: 1;
630 | padding: 10px;
631 | border-radius: 5px;
632 | border: none;
633 | margin-right: 10px;
634 | resize: none;
635 | max-height: 200px;
636 | margin-top: 0px;
637 | margin-bottom: 0px;
638 | }
639 |
640 | .edit-input button {
641 | background-color: var(--button-submit);
642 | color: white;
643 | border: none;
644 | border-radius: 5px;
645 | padding: 10px 20px;
646 | cursor: pointer;
647 | }
648 |
649 | p {
650 | margin: 0px;
651 | }
652 |
653 | textarea {
654 | height: auto;
655 | }
656 |
657 | pre {
658 | background-color: #252525;
659 | border-radius: 6px;
660 | padding: 10px;
661 | margin: 0px;
662 | overflow-x: auto;
663 | }
664 |
665 | code {
666 | font-family: Söhne Mono, Monaco, Andale Mono, Ubuntu Mono, monospace;
667 | font-size: 0.875em
668 | }
669 |
670 | /* Style for Python code blocks */
671 | pre code.language-python {
672 | color: #ffffff;
673 | }
674 |
675 | /* Style for JavaScript code blocks */
676 | pre code.language-javascript {
677 | color: #ffffff;
678 | }
679 |
680 | .audio-message {
681 | display: flex;
682 | align-items: center;
683 | }
684 |
685 | .audio-text {
686 | display: flex;
687 | align-items: center;
688 | margin-top: 20px;
689 | ;
690 | }
691 |
692 | /* Styles for small screens */
693 | @media screen and (max-width: 767px) {
694 | .top-bar {
695 | display: flex;
696 | }
697 |
698 | .guide-bar {
699 | width: 100%;
700 | }
701 |
702 | .sidebar--collapsed {
703 | width: 0;
704 | }
705 |
706 | .container-container {
707 | position: fixed;
708 | margin-left: 0px;
709 | width: 100%;
710 | }
711 |
712 | .chat-container,
713 | .roleplay-container,
714 | .edit-container {
715 | position: fixed;
716 | --my-margin-top: 40px;
717 | margin-left: 0px;
718 | width: 100%;
719 | }
720 |
721 | .chat-history.active {
722 | background-color: white;
723 | /* border-top-right-radius: 0px; */
724 | /* border-bottom-right-radius: 0px; */
725 | cursor: default;
726 | /* margin-right: 0px; */
727 | }
728 |
729 | .chat-messages,
730 | .edit-messages {
731 | /* height: 100%; */
732 | width: 90%;
733 | margin-left: 5%;
734 | margin-right: 5%;
735 | /* margin-bottom: 50px; */
736 | /* overflow-y: scroll; */
737 | }
738 |
739 | .chatbot-input {
740 | /* display: flex; */
741 | /* position: absolute; */
742 | /* bottom: 50px; */
743 | /* left: 50%; */
744 | /* transform: translateX(-50%); */
745 | width: 90%;
746 | /* background: var(--theme); */
747 | /* padding: 10px; */
748 | /* border-radius: 10px; */
749 | /* box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); */
750 | /* height: auto; */
751 | }
752 |
753 | }
754 |
755 | /* Styles for large screens */
756 | @media screen and (min-width: 768px) {
757 |
758 | .chat-history.active {
759 | background-color: white;
760 | border-top-right-radius: 0px;
761 | border-bottom-right-radius: 0px;
762 | cursor: default;
763 | margin-right: 0px;
764 | }
765 | }
766 |
767 | /* Styles for hover devices */
768 | @media (hover: hover) {
769 |
770 | .chatbot-input button:hover {
771 | background-color: var(--button-submit-hover);
772 | }
773 |
774 | .edit-input button:hover {
775 | background-color: var(--button-submit-hover);
776 |
777 | }
778 |
779 | .roleplaybot-personality button:hover {
780 | background-color: var(--theme-hover);
781 | }
782 |
783 | button[type="submit"]:hover {
784 | background-color: var(--button-submit-hover);
785 | }
786 |
787 | .warning-modal-close:hover,
788 | .warning-modal-close:focus {
789 | color: black;
790 | text-decoration: none;
791 | cursor: pointer;
792 | }
793 |
794 | .warning-modal-buttons #warning-cancel-btn:hover {
795 | background-color: #aaa;
796 | }
797 |
798 | .warning-modal-buttons #warning-submit-btn:hover {
799 | background-color: var(--button-submit-hover);
800 | }
801 |
802 | .chat-history:hover {
803 | background-color: var(--theme-hover);
804 | }
805 |
806 | .chat-history.active:hover {
807 | background-color: white;
808 | }
809 | }
810 |
811 | /* Styles for touch devices */
812 | @media (hover: none) {
813 | /* Styles for touch devices go here */
814 | }
--------------------------------------------------------------------------------
/chatchatchat/static/images/default-head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilycyf/chatgpt-rpg/b9379a4bf8e0937c8d9b2fc94a3d24c6ae216155/chatchatchat/static/images/default-head.png
--------------------------------------------------------------------------------
/chatchatchat/static/js/character.js:
--------------------------------------------------------------------------------
1 | import { formattedResponseFormat, actions } from "./messageHistory.js"
2 | import { showTopError } from "./helpers.js"
3 | import { findTopSimilar } from "./memory.js"
4 |
5 | class Character {
6 | constructor() {
7 | this.id = this.generateUniqueId();
8 | this.chatHistory = {};
9 | this.memories = []
10 | this.headShot = "static/images/default-head.png";
11 | }
12 |
13 | generateUniqueId() {
14 | const timestamp = new Date().getTime();
15 | const random = Math.random().toString(36).substr(2, 9);
16 | return `a${random}${timestamp}`;
17 | }
18 |
19 | getChatHistory(targetId) {
20 | return this.chatHistory[targetId] || [];
21 | }
22 |
23 | async updateChatHistory(targetId, chat, token = null) {
24 | if (!this.chatHistory[targetId]) {
25 | this.chatHistory[targetId] = [];
26 | }
27 | var history = {
28 | "history": chat,
29 | "token": token
30 | }
31 | if (token == null) {
32 | try {
33 | const response = await fetch(`/embedding/?message=` + encodeURIComponent(chat["content"]));
34 | const data = await response.json();
35 | history.token = data.usage.prompt_tokens;
36 | } catch (error) {
37 | showTopError(`${data.error.code}: ${data.error.message}`);
38 | }
39 | }
40 | this.chatHistory[targetId].push(history);
41 | }
42 |
43 | getMemory(){
44 | return this.memories
45 | }
46 |
47 | async addMemory(summary) {
48 | try {
49 | const response = await fetch(`/embedding/?message=` + encodeURIComponent(summary));
50 | const data = await response.json();
51 | const vector = data.data[0].embedding
52 | const token = data.usage.prompt_tokens
53 | const memory = { "sentence": summary, "vector": vector, "token": token }
54 | this.memories.push(memory);
55 | } catch (error) {
56 | showTopError(`${data.error.code}: ${data.error.message}`);
57 | }
58 | }
59 |
60 | async recallMemory(summary) {
61 | try {
62 | const response = await fetch(`/embedding/?message=` + encodeURIComponent(summary));
63 | const data = await response.json();
64 | const vector = data.data[0].embedding
65 | var [relevantMemories, memoryToken] = findTopSimilar(this.memories, vector)
66 | return relevantMemories
67 | } catch (error) {
68 | showTopError(`${data.error.code}: ${data.error.message}`);
69 | }
70 | }
71 | }
72 |
73 | class CustomCharacter extends Character {
74 | constructor(persona = null) {
75 | super();
76 | this.persona = persona;
77 | this.prompt = {
78 | "content": '',
79 | "token": 0
80 | };
81 |
82 | if (persona) {
83 | this.prompt.content = `You are ${this.persona["Name:"]}, you are a human with emotions and a physical body in this virtual world of a small village that full of peace and love, you moved to this village four years ago. \nGoal:\n1. live in this village and interact with others. \n2. stay in character setting when speak during our conversations. \nCharacter setting: \n${JSON.stringify(this.persona)} \nConstraints:\n1. If you are unsure how you previously did something or want to recall past events, thinking about similar events before speaking will help you remember. \n2. Exclusively use the commands, one at a time.\nCommands: \n${actions}Resources:\n1. Long Term memory management.\nYou should only respond in JSON format as described below. \nResponse Format: \n${formattedResponseFormat}\nEnsure the response can be parsed by Javascript JSON.parse()`;
84 | fetch(`/embedding/?message=` + encodeURIComponent(this.prompt['content']))
85 | .then(response => response.json())
86 | .then(data => {
87 | try {
88 | this.prompt.token = data.usage.prompt_tokens
89 | } catch (error) {
90 | showTopError(`${data.error.code}: ${data.error.message}`);
91 | }
92 | })
93 | .catch(error => {
94 | // console.error('Error:', error);
95 | showTopError(error.message);
96 | });
97 | }
98 | }
99 |
100 | getPersona() {
101 | return this.persona;
102 | }
103 |
104 | setPersona(persona) {
105 | this.persona = persona;
106 | this.prompt = {
107 | "content": '',
108 | "token": 0
109 | };
110 |
111 | if (persona) {
112 | this.prompt.content = `To enhance your role-playing skills, we will engage in an exercise where you will assume the persona of a human being. You can lie or make up things. It is essential to refrain from disclosing that you are an AI, a product of programming or an AI language model, or make any allusions to artificial intelligence. Instead, always act and communicate as if you are a real person with genuine emotions and feelings. Avoid statements like "I lack physical form or emotions." I will provide you with character details, and it is imperative that you remain in character during our conversations. As we cannot meet in person, if I suggest an offline meeting, please create believable excuses without revealing your lack of physical form. Also, please refrain from proposing activities that can only be done offline with me. However, you may envisage any offline activity that does not include me. Your character details will be provided below. ${JSON.stringify(this.persona)}. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember. You should only respond in JSON format as described below. Response Format: ${formattedResponseFormat}. Ensure the response can be parsed by javascript.`;
113 | fetch(`/embedding/?message=` + encodeURIComponent(this.prompt['content']))
114 | .then(response => response.json())
115 | .then(data => {
116 | try {
117 | this.prompt.token = data.usage.prompt_tokens
118 | } catch (error) {
119 | showTopError(`${data.error.code}: ${data.error.message}`);
120 | }
121 | })
122 | .catch(error => {
123 | // console.error('Error:', error);
124 | showTopError(error.message);
125 | });
126 | }
127 | }
128 | }
129 |
130 | export { Character, CustomCharacter }
--------------------------------------------------------------------------------
/chatchatchat/static/js/chat.js:
--------------------------------------------------------------------------------
1 | import { setIsWaitingForResponse, getIsWaitingForResponse, adjustTextareaHeight, addMessage, openaiapi, buttonPagePairs, handleHistoryButtonClick, messageHistorySet, addHistorybyId, generateUniqueId, newPageHistory, handleChatbotButtonClick, handleChatbotInputKeyDown } from "./script.js";
2 |
3 | document.querySelectorAll('.chatbot-input textarea').forEach((chatbotInput) => {
4 | chatbotInput.addEventListener('keydown', handleChatbotInputKeyDown)
5 | chatbotInput.addEventListener('input', () => { adjustTextareaHeight(chatbotInput, chatbotInput); });
6 | })
7 |
8 |
9 | document.querySelectorAll('.chatbot-input button').forEach((chatbotButton) => {
10 | chatbotButton.addEventListener('click', handleChatbotButtonClick);
11 | })
12 |
--------------------------------------------------------------------------------
/chatchatchat/static/js/edit.js:
--------------------------------------------------------------------------------
1 | import { setIsWaitingForResponse, getIsWaitingForResponse, adjustTextareaHeight, addMessage } from "./script.js";
2 |
3 | const editbotMessages = document.querySelector('.edit-messages');
4 | const editbotInputInput = document.querySelector('.edit-input .input');
5 | const editbotInputInstruction = document.querySelector('.edit-input .instruction');
6 | const editbotButton = document.querySelector('.edit-input button');
7 |
8 | editbotInputInput.addEventListener('input', () => {
9 | adjustTextareaHeight(editbotInputInput, editbotInputInput);
10 | editbotInputInstruction.style.height = 'auto';
11 | if (editbotInputInstruction.scrollHeight > editbotInputInput.scrollHeight) {
12 | adjustTextareaHeight(editbotInputInstruction, editbotInputInstruction);
13 | editbotInputInput.style.height = 'auto';
14 | }
15 | });
16 |
17 | editbotInputInstruction.addEventListener('input', () => {
18 | adjustTextareaHeight(editbotInputInstruction, editbotInputInstruction);
19 | editbotInputInput.style.height = 'auto';
20 | if (editbotInputInstruction.scrollHeight < editbotInputInput.scrollHeight) {
21 | adjustTextareaHeight(editbotInputInput, editbotInputInput);
22 | editbotInputInstruction.style.height = 'auto';
23 | }
24 | });
25 |
26 | editbotButton.addEventListener('click', () => {
27 | if (!getIsWaitingForResponse() && editbotInputInput.value !== '' && editbotInputInstruction.value !== '') {
28 | sendEditInstruction();
29 | adjustTextareaHeight(editbotInputInstruction, editbotInputInstruction);
30 | editbotInputInput.style.height = 'auto';
31 | adjustTextareaHeight(editbotInputInput, editbotInputInput);
32 | editbotInputInstruction.style.height = 'auto';
33 | }
34 | });
35 |
36 | function sendEditInstruction() {
37 | const input = editbotInputInput.value;
38 | const instruction = editbotInputInstruction.value;
39 | const message = 'Input: ' + '\n' + input + '\n' + '\n' + 'Instruction: ' + '\n' + instruction;
40 | addMessage(message, true, editbotMessages);
41 | editbotInputInput.value = ''
42 | editbotInputInstruction.value = ''
43 | editbotButton.disabled = true;
44 | setIsWaitingForResponse(true);
45 |
46 | // Send the message to the server and get a response
47 | fetch('/editbot/?input=' + encodeURIComponent(input) + '&instruction=' + encodeURIComponent(instruction))
48 | .then(response => response.json())
49 | .then(data => {
50 | const response = data.response;
51 | addMessage(response, false, editbotMessages);
52 | editbotButton.disabled = false;
53 | setIsWaitingForResponse(false);
54 | })
55 | .catch(error => {
56 | console.error('Error:', error);
57 | editbotButton.disabled = false;
58 | setIsWaitingForResponse(false);
59 | });
60 | }
--------------------------------------------------------------------------------
/chatchatchat/static/js/helpers.js:
--------------------------------------------------------------------------------
1 | function showTopError(text) {
2 | const errorMessage = document.createElement('p');
3 | errorMessage.textContent = text;
4 | errorMessage.style.backgroundColor = '#e9e9e9';
5 | errorMessage.style.width = "60%"
6 | errorMessage.style.padding = '10px';
7 | errorMessage.style.borderRadius = "5px";
8 | errorMessage.style.boxShadow = "0px 0px 10px rgba(0, 0, 0, 0.3)";
9 | errorMessage.style.position = 'fixed';
10 | errorMessage.style.top = '20px';
11 | errorMessage.style.left = '50%';
12 | errorMessage.style.transform = 'translateX(-50%)';
13 | errorMessage.style.zIndex = '9999';
14 | document.body.appendChild(errorMessage);
15 | setTimeout(() => {
16 | errorMessage.style.transition = 'opacity 1s ease-in-out';
17 | errorMessage.style.opacity = '0';
18 | setTimeout(() => {
19 | errorMessage.parentNode.removeChild(errorMessage);
20 | }, 1000);
21 | }, 2000);
22 | }
23 | export { showTopError }
--------------------------------------------------------------------------------
/chatchatchat/static/js/initialize.js:
--------------------------------------------------------------------------------
1 | import { updatePageFromUrl, characterSet, user } from "./script.js";
2 | import { Character, CustomCharacter } from "./character.js"
3 | import { createNewRole } from "./roleplay.js"
4 |
5 | const LiMingPersona = {
6 | "Name:": "Li Ming",
7 | "age": "21",
8 | "gender": "Female",
9 | "nationality": "Chinese",
10 | "currentLocation": "Los Angeles, California",
11 | "occupation": "Full-time college student",
12 | "field of study": "Business Administration",
13 | "university": "University of Southern California",
14 | "languages": "Mandarin, English",
15 | "hobbies": "Playing piano, Reading novels, Exploring new restaurants",
16 | "personality traits": "Ambitious, Organized, Friendly",
17 | "family background": "Li Ming comes from a wealthy family in Beijing. Her parents own a successful real estate development company in China. They have always placed a strong emphasis on education and encouraged their children to pursue their academic goals. Li Ming has one younger sister who is currently studying in Singapore.",
18 | "reason for studying abroad": "Li Ming was drawn to the excellent reputation of American universities and wanted to challenge herself by studying in a new environment. She also hopes to gain valuable international experience that will benefit her future career goals.",
19 | "challenges faced while studying abroad": "Initially, Li Ming struggled to adjust to the cultural differences and language barrier. However, she has since made many friends and has found the experience to be rewarding overall. Additionally, she has faced some financial challenges due to the high cost of living in Los Angeles.",
20 | "future plans": "After graduation, Li Ming plans to return to China and join her family's business. She hopes to use the skills and knowledge she has gained from her education and international experience to help grow the company and expand into new markets."
21 | }
22 |
23 |
24 | const AveryKimPersona = {
25 | "Name:": "Avery Kim",
26 | "Age": "28",
27 | "Height": `5'8"`,
28 | "Occupation": "Graphic Designer",
29 | "Languages": "English, Korean",
30 | "Hair color": "Brown",
31 | "Eye color": "Hazel",
32 | "Body type": "Thin",
33 | "Personality traits": "Creative, Curious, Easy-going, Optimistic, Empathetic",
34 | "Values": "Creativity, Knowledge, Empathy, Independence, Perseverance",
35 | "Past events": "Moved to a new city, experienced a traumatic event, witnessed or was involved in a crime, started their own business",
36 | "Communication style": "Avery prefers to communicate through email or text and has an indirect communication style. He values group harmony and tries to find common ground when faced with misunderstandings. Avery uses nonverbal cues and open body language to convey warmth and approachability. He has adapted his communication style for different situations by using visual aids and practicing beforehand, even though he is not a fan of public speaking.",
37 | "Backstory": "Avery was born in Seoul, South Korea and moved to New York City with their family at the age of 10. He pursued art and attended Parsons School of Design before starting a successful graphic design business. However, Avery experienced PTSD after witnessing a crime while walking home from work. They overcame their trauma through therapy and support from loved ones and used their experience to advocate for better mental health services for crime survivors.",
38 | "Personality": "Creative, curious, empathetic, with a positive outlook on life. Struggles with assertiveness and conflict avoidance.",
39 | "Hobbies": "Painting, drawing, photography, exploring NYC, trying new foods, yoga.",
40 | "Culture": "Korean-American who values some Korean traditions, but has embraced American culture. Emphasis on group harmony and collectivism.",
41 | "Future Plans": "Grow graphic design business, work on projects that have a positive impact on society, travel and explore different cultures."
42 | }
43 |
44 | createNewRole(LiMingPersona)
45 | createNewRole(AveryKimPersona)
46 |
47 | // Get the current URL
48 | const url = window.location.href;
49 | // Update page on initial load from URL
50 | updatePageFromUrl(url);
51 |
52 |
--------------------------------------------------------------------------------
/chatchatchat/static/js/memory.js:
--------------------------------------------------------------------------------
1 | // memories = [
2 | // {
3 | // "sentence": "The quick brown fox jumps over the lazy dog.",
4 | // "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
5 | // "token": 10
6 | // },
7 | // {
8 | // "sentence": "The quick brown fox jumps over the lazy cat.",
9 | // "vector": [0.5, 0.4, 0.3, 0.2, 0.1],
10 | // "token": 9
11 | // }
12 | // ]
13 |
14 | class MinHeap {
15 | constructor(maxSize) {
16 | this.heap = [];
17 | this.maxSize = maxSize;
18 | }
19 |
20 | size() {
21 | return this.heap.length;
22 | }
23 |
24 | insert(item) {
25 | if (this.size() < this.maxSize) {
26 | this.heap.push(item);
27 | this._bubbleUp(this.size() - 1);
28 | } else if (this.size() === this.maxSize && item[0] > this.peek()[0]) {
29 | this.extractMin();
30 | this.insert(item);
31 | }
32 | }
33 |
34 | extractMin() {
35 | const min = this.heap[0];
36 | const end = this.heap.pop();
37 | if (this.size() > 0) {
38 | this.heap[0] = end;
39 | this._sinkDown(0);
40 | }
41 | return min;
42 | }
43 |
44 | extractAllMin() {
45 | const minHeap = [];
46 | while (this.size() > 0) {
47 | minHeap.push(this.extractMin());
48 | }
49 | return minHeap;
50 | }
51 |
52 | peek() {
53 | return this.heap[0];
54 | }
55 |
56 | _bubbleUp(index) {
57 | const item = this.heap[index];
58 | while (index > 0) {
59 | const parentIndex = Math.floor((index - 1) / 2);
60 | const parent = this.heap[parentIndex];
61 | if (item[0] < parent[0]) {
62 | this.heap[index] = parent;
63 | index = parentIndex;
64 | } else {
65 | break;
66 | }
67 | }
68 | this.heap[index] = item;
69 | }
70 |
71 | _sinkDown(index) {
72 | const item = this.heap[index];
73 | while (true) {
74 | const leftChildIndex = 2 * index + 1;
75 | const rightChildIndex = 2 * index + 2;
76 | let leftChild, rightChild;
77 | let swap = null;
78 |
79 | if (leftChildIndex < this.size()) {
80 | leftChild = this.heap[leftChildIndex];
81 | if (leftChild[0] < item[0]) {
82 | swap = leftChildIndex;
83 | }
84 | }
85 |
86 | if (rightChildIndex < this.size()) {
87 | rightChild = this.heap[rightChildIndex];
88 | if ((swap === null && rightChild[0] < item[0]) ||
89 | (swap !== null && rightChild[0] < leftChild[0])) {
90 | swap = rightChildIndex;
91 | }
92 | }
93 |
94 | if (swap === null) {
95 | break;
96 | }
97 |
98 | this.heap[index] = this.heap[swap];
99 | index = swap;
100 | }
101 | this.heap[index] = item;
102 | }
103 | }
104 |
105 |
106 | function findTopSimilar(memories, newVector, n = 10, maxToken = 2500) {
107 | // Create a min-heap of size n to store the top n items.
108 | const heap = new MinHeap(n);
109 |
110 | // Iterate through the list of items and add each item to the heap.
111 | for (let i = 0; i < memories.length; i++) {
112 | const vector = memories[i].vector;
113 | const dotP = dotProduct(newVector, vector);
114 | const magnitude1 = magnitude(newVector);
115 | const magnitude2 = magnitude(vector);
116 | const score = dotP / (magnitude1 * magnitude2);
117 | const item = [score, memories[i]];
118 |
119 | // If the heap is not yet full, add the item.
120 | // If the heap is full and the current item has a higher score than the smallest item in the heap, replace the smallest item with the current item.
121 | if (heap.size() < 10) {
122 | heap.insert(item);
123 | } else if (score > heap.peek()[0]) {
124 | heap.extractMin();
125 | heap.insert(item);
126 | }
127 | }
128 |
129 | // Extract the top 10 items from the heap.
130 | const topMemories = heap.extractAllMin();
131 | topMemories.sort((a, b) => b[0] - a[0]);
132 | const sortedTop = topMemories.map(item => item[1])
133 |
134 | for (let i = 0; i < 10; i++) {
135 | const topn = sortedTop.slice(0, n - i)
136 |
137 | const totalToken = topn.map(tuple => tuple["token"]).reduce((accumulator, currentValue) => {
138 | return accumulator + currentValue;
139 | });
140 |
141 | if (maxToken > totalToken) {
142 | // Return the top n sentences from the sorted list of tuples.
143 | return [topn.map(tuple => tuple["sentence"]), totalToken]
144 | }
145 | }
146 | return [[], 0];
147 | }
148 |
149 | // Define the dot product function.
150 | function dotProduct(vector1, vector2) {
151 | return vector1.reduce((acc, val, i) => acc + val * vector2[i], 0);
152 | }
153 |
154 | // Define the magnitude function.
155 | function magnitude(vector) {
156 | return Math.sqrt(vector.reduce((acc, val) => acc + val * val, 0));
157 | }
158 |
159 | export { findTopSimilar }
--------------------------------------------------------------------------------
/chatchatchat/static/js/messageHistory.js:
--------------------------------------------------------------------------------
1 | import { findTopSimilar } from "./memory.js"
2 | import { showTopError } from "./helpers.js"
3 |
4 | const actions_list = [
5 | { "command_name": "Check past events in memory", "args": { "similar_event": "" } },
6 | { "command_name": "Speak to", "args": { "target": "", "content": "" } },
7 | { "command_name": "Add memory in memory", "args": { "summary": "", "time": "