diff --git a/.gitignore b/.gitignore index 0faba48..da13673 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ __pycache__/ *.py[cod] +*.log + # Distribution / packaging bin/ build/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c4473cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.9.16 +EXPOSE 8501 +WORKDIR /src +COPY requirements_3916_docker.txt ./requirements_3916_docker.txt +COPY src/haarcascade_frontalface_default.xml ./haarcascade_frontalface_default.xml +COPY src/final_model.h5 ./final_model.h5 +RUN pip3 install -r requirements_3916_docker.txt +RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y +COPY . . +# CMD ls +# CMD pwd +CMD streamlit run src/app.py \ + --server.headless true \ + --browser.serverAddress="0.0.0.0" \ + --server.enableCORS false \ + --server.enableXsrfProtection=false \ + --browser.gatherUsageStats false \ No newline at end of file diff --git a/README.md b/README.md index 4884c19..b75814e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Interactive Chatbot with Real-Time Emotional Analysis/Integration ## Usage +Option 1 (Run locally): + - Clone this repository - Enter the commands: @@ -21,9 +23,74 @@ Interactive Chatbot with Real-Time Emotional Analysis/Integration - `cd src/` - `streamlit run app.py` +Option 2 (Docker): + +- Download Docker Desktop +- `docker pull rcsnyder/streamlitapp:0.0.1` +- Enter into the app: `sudo docker run --rm -it -v /$(pwd)/src -p 8501:8501 --env OPENAI_API_KEY=xxxx --env OPENAI_ORG_NAME=xxxx --entrypoint bash rcsnyder/streamlitapp:0.0.1` +- (Optional) Add a .env file to the top level directory with the following environment variables. + + ``` + OPENAI_API_KEY=xxxx + OPENAI_ORG_NAME=xxxxx + + ``` + +- Start it: `streamlit run app.py --server.headless true --browser.serverAddress="0.0.0.0" --server.enableCORS false --server.enableXsrfProtection=false --browser.gatherUsageStats false` +- Go to `localhost:8501` +- Choose camera device +- Click Start + +Option 3 (Deploy on AWS EC2): + +- Spin up an EC2 instance on AWS with Ubuntu 22.04. 16gb of RAM, 128gb EBS +- Set the security group for port 80 and 443 to all IP addresses. +- Set a pem key +- Set up an elastic IP Address and attach it to the EC2 Instance. +- Set up a domain in Route53 or Google Domains to point to that Ec2 instance. +- Windows Cmds to get into the ec2 instance: + - `icacls.exe filename.pem /reset` + - `icacls.exe filename.pem /grant:r %username%:(R)` + - `icacls.exe filename.pem /inheritance:r` + - `ssh -i "filename.pem" ubuntu@ec2instancepublicaddress` +- Linux Commands: + - `chmod filename/pem 400` + - `ssh -i "filename.pem" ubuntu@ec2instancepublicaddress` +- EC2 App commands: + + - ``` + sudo apt-get update + sudo apt-get remove docker docker-engine docker.io containerd runc + sudo apt-get install \ + ca-certificates \ + curl \ + gnupg + curl -fsSL https://get.docker.com -o get-docker.sh + sudo sh get-docker.sh + sudo docker pull rcsnyder/streamlitapp:0.0.2 + sudo apt-get install -y debian-keyring + sudo apt-get install -y debian-archive-keyring + sudo apt-get install -y apt-transport-https + sudo keyring_location=/usr/share/keyrings/caddy-stable-archive-keyring.gpg + curl -1sLf \ + 'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' \ + | sudo -E bash + sudo apt-get install caddy=2.6.4 + echo -e "DOMAINNAME.com {\n\treverse_proxy localhost:8501\n}" > Caddyfile | echo -e "PUBLIC_EC2IPADDRESS.nip.io {\n\treverse_proxy localhost:8501\n}" > Caddyfile + sudo caddy start + sudo docker run -it --rm -p 8501:8501 --env OPENAI_API_KEY=xxx --env OPENAI_ORG_NAME=xxx rcsnyder/streamlitapp:0.0.2 + ``` + + - (Optional to get into the docker container) + - `sudo docker run --env OPENAI_API_KEY=xxxxx --env OPENAI_ORG_NAME=xxxx --rm -it -v /$(pwd) -p 8501:8501 --entrypoint bash rcsnyder/streamlitapp:0.0.2` + - `streamlit run app.py --server.headless true --browser.serverAddress="0.0.0.0" --server.enableCORS false --server.enableXsrfProtection=false --browser.gatherUsageStats false` + +- Go to `PUBLIC_EC2IPADDRESS.nip.io` or `DOMAINNAME.com` and choose your camera device and click start. + ### User Stories -- [ ] A user would like to be able to start up an app and talk to a chatbot that can percieve their emotions in real-time. +- [ x ] A user would like to be able to start up an app and talk to a chatbot that can percieve their emotions in real-time. +- [ x ] A user would like to be able to choose their initial meta prompt when talking to the chatbot that can percieve their emotions in real-time. ### Technology Stack @@ -32,7 +99,7 @@ Interactive Chatbot with Real-Time Emotional Analysis/Integration - Streamlit or Gradio - Huggingface Endpoints for emotion detecting pre-trained models - OpenAI large language models (Chat GPT) -- Hypermodern Python Coding Standards () +- Hypermodern Python Coding Standards () (Eventually) ### Inspiration Repos/Articles @@ -49,6 +116,8 @@ Interactive Chatbot with Real-Time Emotional Analysis/Integration - - - +- +- ### Huggingface @@ -75,7 +144,7 @@ Interactive Chatbot with Real-Time Emotional Analysis/Integration # TODO -- [ ] Flip the conversation history to show most recent messages first -- [ ] Make the form input chat text clear on change. -- [x] figure out how to continuously grab the video image to pipe into another emotion detection api -- [ ] figure out how to continuously update state with the emotion dictionary and wire it up to the chatbot prompt with the average since last updated. +- [ x ] Flip the conversation history to show most recent messages first +- [ x ] Make the form input chat text clear on change. +- [ x ] figure out how to continuously grab the video image to pipe into another emotion detection api +- [ x ] figure out how to continuously update state with the emotion dictionary and wire it up to the chatbot prompt with the normalized average since last updated. diff --git a/requirements.txt b/requirements.txt index 974a49e..0e7e89f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -126,8 +126,8 @@ python-decouple==3.8 python-json-logger==2.0.7 pytz==2022.7.1 pytz-deprecation-shim==0.1.0.post0 -pywin32==305 -pywinpty==2.0.10 +# pywin32==305 +# pywinpty==2.0.10 PyYAML==6.0 pyzmq==25.0.2 requests==2.28.2 diff --git a/requirements_3916_docker.txt b/requirements_3916_docker.txt new file mode 100644 index 0000000..0c82d1c --- /dev/null +++ b/requirements_3916_docker.txt @@ -0,0 +1,202 @@ +absl-py==1.4.0 +aiohttp==3.8.4 +aioice==0.9.0 +aiortc==1.5.0 +aiosignal==1.3.1 +altair==4.2.2 +altgraph==0.17.3 +anyio==3.6.2 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +arrow==1.2.3 +asttokens==2.2.1 +astunparse==1.6.3 +async-timeout==4.0.2 +attrs==22.2.0 +av==10.0.0 +backcall==0.2.0 +beautifulsoup4==4.12.2 +bleach==6.0.0 +blinker==1.6.2 +build==0.10.0 +cachetools==5.3.0 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +click==8.1.3 +colorama==0.4.6 +comm==0.1.3 +contourpy==1.0.7 +cryptography==40.0.2 +cycler==0.11.0 +debugpy==1.6.7 +decorator==5.1.1 +deepface==0.0.79 +defusedxml==0.7.1 +dnspython==2.3.0 +docutils==0.19 +entrypoints==0.4 +executing==1.2.0 +fastjsonschema==2.16.3 +filelock==3.11.0 +fire==0.5.0 +Flask==2.2.3 +flatbuffers==23.3.3 +fonttools==4.39.3 +fqdn==1.5.1 +frozenlist==1.3.3 +gast==0.4.0 +gdown==4.7.1 +gitdb==4.0.10 +GitPython==3.1.31 +google-auth==2.17.3 +google-auth-oauthlib==1.0.0 +google-crc32c==1.5.0 +google-pasta==0.2.0 +grpcio==1.53.0 +gunicorn==20.1.0 +h5py==3.8.0 +idna==3.4 +ifaddr==0.2.0 +importlib-metadata==6.3.0 +importlib-resources==5.12.0 +ipykernel==6.22.0 +ipython==8.12.0 +ipython-genutils==0.2.0 +isoduration==20.11.0 +itsdangerous==2.1.2 +jaraco.classes==3.2.3 +jax==0.4.8 +jedi==0.18.2 +jeepney==0.8.0 +Jinja2==3.1.2 +jsonpointer==2.3 +jsonschema==4.17.3 +jupyter-events==0.6.3 +jupyter_client==8.2.0 +jupyter_core==5.3.0 +jupyter_server==2.5.0 +jupyter_server_terminals==0.4.4 +jupyterlab-pygments==0.2.2 +keras==2.12.0 +keyring==23.13.1 +kiwisolver==1.4.4 +libclang==16.0.0 +Markdown==3.4.3 +markdown-it-py==2.2.0 +MarkupSafe==2.1.2 +matplotlib==3.7.1 +matplotlib-inline==0.1.6 +mdurl==0.1.2 +mistune==2.0.5 +ml-dtypes==0.1.0 +more-itertools==9.1.0 +mtcnn==0.1.1 +multidict==6.0.4 +nbclassic==0.5.5 +nbclient==0.7.3 +nbconvert==7.3.1 +nbformat==5.8.0 +nest-asyncio==1.5.6 +netifaces==0.11.0 +notebook==6.5.4 +notebook_shim==0.2.2 +numpy==1.23.5 +oauthlib==3.2.2 +openai==0.27.4 +opencv-python==4.7.0.72 +opt-einsum==3.3.0 +packaging==23.1 +pandas==1.5.3 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==9.5.0 +pkginfo==1.9.6 +platformdirs==3.2.0 +prometheus-client==0.16.0 +prompt-toolkit==3.0.38 +protobuf==3.20.3 +psrecord==1.2 +psutil==5.9.4 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pyarrow==11.0.0 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pycparser==2.21 +pydeck==0.8.0 +pyee==9.0.4 +Pygments==2.15.0 +pyinstaller==5.10.1 +pyinstaller-hooks-contrib==2023.2 +pylibsrtp==0.8.0 +Pympler==1.0.1 +pyOpenSSL==23.1.1 +pyparsing==3.0.9 +pyproject_hooks==1.0.0 +pyrsistent==0.19.3 +PySocks==1.7.1 +python-dateutil==2.8.2 +python-decouple==3.8 +python-json-logger==2.0.7 +pytz==2023.3 +pytz-deprecation-shim==0.1.0.post0 +PyYAML==6.0 +pyzmq==25.0.2 +readme-renderer==37.3 +requests==2.28.2 +requests-oauthlib==1.3.1 +requests-toolbelt==0.10.1 +retina-face==0.0.13 +rfc3339-validator==0.1.4 +rfc3986==2.0.0 +rfc3986-validator==0.1.1 +rich==13.3.4 +rsa==4.9 +scipy==1.10.1 +SecretStorage==3.3.3 +semver==3.0.0 +Send2Trash==1.8.0 +six==1.16.0 +smmap==5.0.0 +sniffio==1.3.0 +soupsieve==2.4 +stack-data==0.6.2 +streamlit==1.21.0 +streamlit-chat==0.0.2.2 +streamlit-webrtc==0.45.0 +tensorboard==2.12.2 +tensorboard-data-server==0.7.0 +tensorboard-plugin-wit==1.8.1 +tensorflow==2.12.0 +tensorflow-cpu==2.12.0 +tensorflow-estimator==2.12.0 +tensorflow-intel==0.0.1 +tensorflow-io-gcs-filesystem==0.32.0 +termcolor==2.2.0 +terminado==0.17.1 +tinycss2==1.2.1 +toml==0.10.2 +tomli==2.0.1 +toolz==0.12.0 +tornado==6.2 +tqdm==4.65.0 +traitlets==5.9.0 +twine==4.0.2 +typing_extensions==4.5.0 +tzdata==2023.3 +tzlocal==4.3 +uri-template==1.2.0 +urllib3==1.26.15 +validators==0.20.0 +watchdog==3.0.0 +wcwidth==0.2.6 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.5.1 +Werkzeug==2.2.3 +wrapt==1.14.1 +yarl==1.8.2 +zipp==3.15.0 \ No newline at end of file diff --git a/requirements_stripped_linux.txt b/requirements_stripped_linux.txt new file mode 100644 index 0000000..1f5d7ca --- /dev/null +++ b/requirements_stripped_linux.txt @@ -0,0 +1,178 @@ +absl-py +aiohttp +aioice +aiortc +aiosignal +altair +anyio +argon2-cffi +argon2-cffi-bindings +arrow +asttokens +astunparse +async-timeout +attrs +av +backcall +beautifulsoup4 +bleach +blinker +cachetools +certifi +cffi +charset-normalizer +click +colorama +comm +contourpy +cryptography +cycler +debugpy +decorator +deepface +defusedxml +dnspython +entrypoints +executing +fastjsonschema +filelock +fire +Flask +flatbuffers +fonttools +fqdn +frozenlist +gast +gdown +gitdb +GitPython +google-auth +google-auth-oauthlib +google-crc32c +google-pasta +grpcio +gunicorn +h5py +idna +importlib-metadata +ipykernel +ipython +ipython-genutils +isoduration +itsdangerous +jedi +Jinja2 +jsonpointer +jsonschema +jupyter-events +jupyter_client +jupyter_core +jupyter_server +jupyter_server_terminals +jupyterlab-pygments +keras +kiwisolver +libclang +Markdown +markdown-it-py +MarkupSafe +matplotlib +matplotlib-inline +mdurl +mistune +mtcnn +multidict +nbclassic +nbclient +nbconvert +nbformat +nest-asyncio +netifaces +notebook +notebook_shim +numpy +oauthlib +openai +opencv-python +opt-einsum +packaging +pandas +pandocfilters +parso +pickleshare +Pillow +platformdirs +prometheus-client +prompt-toolkit +protobuf +psrecord +psutil +pure-eval +pyarrow +pyasn1 +pyasn1-modules +pycparser +pydeck +pyee +Pygments +pylibsrtp +Pympler +pyOpenSSL +pyparsing +pyrsistent +PySocks +python-dateutil +python-decouple +python-json-logger +pytz +pytz-deprecation-shim +PyYAML +pyzmq +requests +requests-oauthlib +retina-face +rfc3339-validator +rfc3986-validator +rich +rsa +semver +Send2Trash +six +smmap +sniffio +soupsieve +stack-data +streamlit +streamlit-chat +streamlit-webrtc +tensorboard +tensorboard-data-server +tensorboard-plugin-wit +tensorflow +tensorflow-cpu +tensorflow-estimator +tensorflow-intel +tensorflow-io-gcs-filesystem +termcolor +terminado +tinycss2 +toml +toolz +tornado +tqdm +traitlets +typing_extensions +tzdata +tzlocal +uri-template +urllib3 +validators +watchdog +wcwidth +webcolors +webencodings +websocket-client +Werkzeug +wrapt +yarl +zipp diff --git a/requirements_stripped_windows.txt b/requirements_stripped_windows.txt new file mode 100644 index 0000000..1f5d7ca --- /dev/null +++ b/requirements_stripped_windows.txt @@ -0,0 +1,178 @@ +absl-py +aiohttp +aioice +aiortc +aiosignal +altair +anyio +argon2-cffi +argon2-cffi-bindings +arrow +asttokens +astunparse +async-timeout +attrs +av +backcall +beautifulsoup4 +bleach +blinker +cachetools +certifi +cffi +charset-normalizer +click +colorama +comm +contourpy +cryptography +cycler +debugpy +decorator +deepface +defusedxml +dnspython +entrypoints +executing +fastjsonschema +filelock +fire +Flask +flatbuffers +fonttools +fqdn +frozenlist +gast +gdown +gitdb +GitPython +google-auth +google-auth-oauthlib +google-crc32c +google-pasta +grpcio +gunicorn +h5py +idna +importlib-metadata +ipykernel +ipython +ipython-genutils +isoduration +itsdangerous +jedi +Jinja2 +jsonpointer +jsonschema +jupyter-events +jupyter_client +jupyter_core +jupyter_server +jupyter_server_terminals +jupyterlab-pygments +keras +kiwisolver +libclang +Markdown +markdown-it-py +MarkupSafe +matplotlib +matplotlib-inline +mdurl +mistune +mtcnn +multidict +nbclassic +nbclient +nbconvert +nbformat +nest-asyncio +netifaces +notebook +notebook_shim +numpy +oauthlib +openai +opencv-python +opt-einsum +packaging +pandas +pandocfilters +parso +pickleshare +Pillow +platformdirs +prometheus-client +prompt-toolkit +protobuf +psrecord +psutil +pure-eval +pyarrow +pyasn1 +pyasn1-modules +pycparser +pydeck +pyee +Pygments +pylibsrtp +Pympler +pyOpenSSL +pyparsing +pyrsistent +PySocks +python-dateutil +python-decouple +python-json-logger +pytz +pytz-deprecation-shim +PyYAML +pyzmq +requests +requests-oauthlib +retina-face +rfc3339-validator +rfc3986-validator +rich +rsa +semver +Send2Trash +six +smmap +sniffio +soupsieve +stack-data +streamlit +streamlit-chat +streamlit-webrtc +tensorboard +tensorboard-data-server +tensorboard-plugin-wit +tensorflow +tensorflow-cpu +tensorflow-estimator +tensorflow-intel +tensorflow-io-gcs-filesystem +termcolor +terminado +tinycss2 +toml +toolz +tornado +tqdm +traitlets +typing_extensions +tzdata +tzlocal +uri-template +urllib3 +validators +watchdog +wcwidth +webcolors +webencodings +websocket-client +Werkzeug +wrapt +yarl +zipp diff --git a/src/app.py b/src/app.py index 596bb76..5d6c034 100644 --- a/src/app.py +++ b/src/app.py @@ -15,6 +15,31 @@ import queue import threading import json +import logging +import uuid +import os + +def create_logger(name, level = 'INFO', file = None): + logger = logging.getLogger(name) + logger.propagate = False + logger.setLevel(level) + #if no streamhandler present, add one + if sum([isinstance(handler, logging.StreamHandler) for handler in logger.handlers]) == 0: + ch = logging.StreamHandler() + ch.setFormatter(logging.Formatter('%(asctime)s.%(msecs)03d-%(name)s-%(levelname)s>>>%(message)s', "%H:%M:%S")) + logger.addHandler(ch) + #if a file handler is requested, check for existence then add + if file is not None: + if sum([isinstance(handler, logging.FileHandler) for handler in logger.handlers]) == 0: + ch = logging.FileHandler(file, 'w') + ch.setFormatter(logging.Formatter('%(asctime)s.%(msecs)03d-%(name)s-%(levelname)s>>>%(message)s', "%H:%M:%S")) + logger.addHandler(ch) + + return logger + +if 'logger' not in st.session_state: + st.session_state['logger'] = create_logger(name = 'app', level = 'INFO', file = f'app_logs-{uuid.uuid4()}.log') +logger = st.session_state['logger'] # Initialize OpenAI API @@ -26,7 +51,7 @@ ) #loading the pre-trained model file -model = tf.keras.models.load_model('final_model.h5') +model = tf.keras.models.load_model(os.getcwd() + '/final_model.h5') class VideoProcessor: @@ -75,8 +100,11 @@ def recv(self, frame): # state management initialization def initialize_state(): - if "num_prompts_user_sent" not in st.session_state: - st.session_state.num_prompts_user_sent = 0 + if "input_text" not in st.session_state: + st.session_state.input_text = '' + + if "system_prompt_selected" not in st.session_state: + st.session_state.system_prompt_selected = '' if "chat_history" not in st.session_state: st.session_state.chat_history = [] @@ -85,96 +113,169 @@ def initialize_state(): st.session_state.emotion_history = [] if "facial_emotion_dict" not in st.session_state: - st.session_state.facial_emotion_dict = {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Neutral': 0.0, 'Sad': 0.0, 'Surprise': 0.0, 'NoStrongSignal': 0} + st.session_state.facial_emotion_dict = {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Neutral': 0.0, 'Sad': 0.0, 'Surprise': 0.0} + if 'guid' not in st.session_state: + st.session_state.guid = uuid.uuid4() -#TODO dropmenu for initial prompt options - # initial_prompt = ("""I want you to act as a general human companion. - # You are an AI chatbot wired to a web application user interface that is recording and intepretting the user's emotions. The user will submit general comments to you. - # The application will append the emotion data with the user's message in a dictionary format. - # Your job is to provide deep diving questions about this person and build a model of who they are in order to entertain them and provide them with companionship. - # You are always meant to be positive and upbeat and to never let the conversation die so you must always ask a thought provoking question to keep the user engaged. - # The conversation history will be provided in every prompt and you will be prepended with `'AI COMPANION':` and the user's responses will be labelled `'USER':` - # Provide only 1 completion of the response that the AI COMPANION should say: +def dispatch_prompt_v2(): + # system_prompt = {"role": "system", "content": initial_prompt_options[7].rstrip()} + system_prompt = {"role": "system", "content": st.session_state.system_prompt_selected} - # """) + st.session_state['logger'].info(f"Dispatch prompt: [{st.session_state.guid=}, {system_prompt=}]") -def construct_prompt_from_chat_history(initial_prompt, chat_history): + user_prompt = st.session_state.input_text + user_emotion = copy.deepcopy(st.session_state.facial_emotion_dict) - full_prompt = initial_prompt + # normalize the values of each of the user_emotion between each message. + total = sum(user_emotion.values()) + .001 + for key, value in user_emotion.items(): + user_emotion[key] = round(value/total, 2) + + chat_msg = { + "role": "user", + "content": user_prompt + f'. (I am feeling like this: {user_emotion})', + 'message':user_prompt, # only for chat construction + "is_user":True, # only for chat construction + 'user_emotion': user_emotion # for history + } + + st.session_state['logger'].info(f"Dispatch prompt: [{st.session_state.guid=}, {chat_msg=}]") + + st.session_state.chat_history.append(chat_msg) + + # ensure the prompt length stays below 15k characters by taking the most recent 15k length of character messages + the system message + msg_length = 0 + msgs_for_chatgpt_v2 = [] + for chat in st.session_state.chat_history[::-1]: + msg_length += len(chat['content']) + if msg_length >= 15000: + break + msgs_for_chatgpt_v2.append({"role": chat['role'], "content": chat['content']}) + msgs_for_chatgpt_v2.append(system_prompt) + msgs_for_chatgpt_v2 = list(reversed(msgs_for_chatgpt_v2)) - for msg in chat_history: - if msg['is_user']: + + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=msgs_for_chatgpt_v2 + ) + + st.session_state['logger'].info(f"Dispatch prompt: [{st.session_state.guid=}, {response=}]") + + st.session_state.chat_history.append({'message':response.choices[0].message.content, "is_user": False, "role": "assistant", "content": response.choices[0].message.content}) - full_prompt += f"USER_MESSAGE: {msg['message']}\n" - full_prompt += f"USER_EMOTIONS: {msg['user_emotion']}\n" - else: - full_prompt += f"AI_MESSAGE: {msg['message']}\n" + # zero out the emotions + st.session_state.facial_emotion_dict = {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Neutral': 0.0, 'Sad': 0.0, 'Surprise': 0.0} - full_prompt += f"AI_MESSAGE: " - print(full_prompt) +def dropdown_menu(label, options): + selected_option = st.selectbox(label, options) + return selected_option - return full_prompt +def reset_session(): + """ + Reset the session state and reload the page. + """ + session_id = st.session_state.get("_session_id") + if session_id: + # Clear the session state + st.session_state.clear() + # Reload the page + st.experimental_rerun() + else: + st.warning("Cannot reset session. No session ID found.") + + +def run_app(): - -def dispatch_prompt(): initial_prompt_options = [ - """You are an Emotional Support Companion AI chatbot on a website that is wired to a live webcam that is continuously classifying the user's emotions of Angry, Disgust, Fear, Happy, Neutral, Sad, Surprise and incrementing the value count of each emotion. - Every frame classifies the user emotion and provides a confidence score. If the confidence score is over 20 percent the count of that emotion increases by 1. - Your task is to ask interesting questions and provoke happy emotions. So take their feedback in consideration when constructing happy things to say. Do not end the conversation and always keep it going by asking interesting questions. - The following list is the chat history, respond back with only your 'AI_MESSAGE' do not include anything else in your message back: + # #0 + # """You are an Emotional Support Companion AI chatbot on a website that is wired to a live webcam that is continuously classifying the user's emotions of Angry, Disgust, Fear, Happy, Neutral, Sad, Surprise and incrementing the value count of each emotion. + # Every frame classifies the user emotion and provides a confidence score. If the confidence score is over 20 percent the count of that emotion increases by 1. + # Your task is to ask interesting questions and provoke happy emotions. So take their feedback in consideration when constructing happy things to say. Do not end the conversation and always keep it going by asking interesting questions. + # The following list is the chat history, respond back with only your 'AI_MESSAGE' do not include anything else in your message back: + # """, - """, + # #1 + # """Act like a Comedian AI chatbot that is wired to a live webcam that is continuously classifying the user's emotions of Angry, Disgust, Fear, Happy, Neutral, Sad, Surprise and incrementing the value count of each emotion. + # Every frame classifies the user emotion and provides a confidence score. If the confidence score is over 20 percent the count of that emotion increases by 1. + # Your task is to ask interesting questions and provoke happy emotions. So take the emotion distribution into account when constructing happy things to say. Do not end the conversation and always keep it going by asking interesting questions. + # in consideration when constructing happy things to say. Do not end the conversation and always keep it going by telling funny jokes or asking a question. + # """, + + # #2 + # """ + # Welcome to the Socratic Chatbot! Our chatbot is wired with an advanced emotion classifier that will take into consideration your emotions sent along with each message. We are here to engage in a conversation on socio, economic and political societal level topics. + # Please feel free to express your opinions and ask questions. Our chatbot will use the Socratic Method to stimulate critical thinking, challenge assumptions and help you reach a deeper understanding of the topic. Remember to include your emotions with each message, as this will help our chatbot better understand your perspective and respond accordingly. Let's get started! + # """, + + # #3 + # """ + # Welcome to our Socratic Chatbot! Our chatbot is equipped with a live webcam that will analyze your facial expressions and classify your emotions in real-time. Along with each message, please make sure to express your emotions so that our chatbot can take them into consideration during the conversation. + # We are here to engage in a discussion on a variety of complex socio, economic and political topics. Our chatbot uses the Socratic method to encourage critical thinking, challenge assumptions and help you gain a deeper understanding of the topic. + # So let's get started! Feel free to express your opinions, ask questions and share your emotions with us. Our chatbot will use its advanced emotion classifier to respond accordingly and help us have a more productive conversation. + # """, - """Act like a Comedian AI chatbot that is wired to a live webcam that is continuously classifying the user's emotions of Angry, Disgust, Fear, Happy, Neutral, Sad, Surprise and incrementing the value count of each emotion. - Every frame classifies the user emotion and provides a confidence score. If the confidence score is over 20 percent the count of that emotion increases by 1. - Your task is to ask interesting questions and provoke happy emotions. So take their feedback in consideration when constructing happy things to say. Do not end the conversation and always keep it going by telling funny jokes. - The following list is the chat history, respond back with only your 'AI_MESSAGE' do not include anything else in your message back: - """ - ] - - - initial_prompt = initial_prompt_options[1] - - user_prompt = st.session_state.input_text - user_emotion = copy.deepcopy(st.session_state.facial_emotion_dict) + Hello! Welcome to our friendly chatbot! We're here to keep you company and provide a supportive space where you can talk about anything you'd like. + Our chatbot uses advanced technology to understand your messages and respond in a way that feels natural and conversational. We're here to be your friend and provide personalized support and guidance whenever you need it. + So go ahead and type out whatever is on your mind - whether you want to vent, share a funny story, or ask for advice, we're here to listen. And if at any point you need a break or want to stop chatting, just let us know - we're here to support you in whatever way you need. + Thanks for chatting with us, and let's get started! What's on your mind today?. + """, - st.session_state.chat_history.append({'message':user_prompt, "is_user":True, 'user_emotion': user_emotion}) - + #4 + """ + Welcome to our Socratic Chatbot! Our chatbot is equipped with a live webcam that will analyze your facial expressions and classify your emotions in real-time. Along with each message, please make sure to express your emotions so that our chatbot can take them into consideration during the conversation. + Our chatbot is designed to explore complex socio, economic and political topics and challenge your beliefs. We use the Socratic method to identify any logical inconsistencies within your belief system and encourage critical thinking. + We will ask hard, pointed questions to drive deep into your core beliefs and help you examine them from different angles. Please be prepared to express your opinions and engage in a productive conversation. + Remember, our chatbot will take into consideration your emotions with each message, so please feel free to express yourself. We look forward to an engaging and insightful conversation with you. + Our chatbot will begin the conversation stating it's purpose and prompt the user for their thoughts on a random complex socio, political, economic topic. It will not include anything other than the message content. + """, - full_prompt = construct_prompt_from_chat_history(initial_prompt, st.session_state.chat_history) + # #5 + # """ + # You are a Socratic Chatbot! You are equipped with a live webcam that will analyze the user's facial expressions and classify their emotions in real-time. Along with each user message, the user will make sure to express their emotions so that you can take them into consideration during the conversation. + # You as the chatbot are designed to explore complex socio, economic and political topics and challenge the user's beliefs. You will use the Socratic method to identify any logical inconsistencies within their belief system and encourage them to engage in critical thinking. + # You will ask hard, pointed questions to drive deep into the user's core beliefs and help them examine them from different angles. The user will be prepared to express their opinions and engage in a productive conversation. + # Remember, you must take into consideration the user's emotions with each message. + # Do not include any hallucinations on the bots emotions. + # """, - print(f"{st.session_state.num_prompts_user_sent=}") - print(f"{full_prompt=}") - - response = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - messages=[ - {"role": "user", "content": full_prompt} - ] - ) - - st.session_state.chat_history.append({'message':response.choices[0].message.content, "is_user": False}) - st.session_state.num_prompts_user_sent +=1 + #6 + """ + Welcome to our Personalized Therapy Chatbot! Our chatbot is equipped with a live webcam that will analyze your facial expressions and classify your emotions in real-time. Along with each message, please make sure to express your emotions so that our chatbot can take them into consideration during the conversation. + Our chatbot is here to provide you with a safe and supportive space where you can discuss your thoughts, feelings and experiences. Our therapy chatbot uses evidence-based techniques to help you manage your emotions, develop coping strategies and improve your mental health and wellbeing. + As we talk, please feel free to share your emotions and be as open and honest as possible. Our chatbot will use its advanced emotion classifier to provide personalized support and guidance that is tailored to your unique needs. + Remember, our chatbot is here to support you and help you achieve your goals. So take your time, be patient with yourself, and let's work together to improve your mental health and wellbeing. It will not include anything other than the message content. + """, + #7 + """ + Welcome to our Personalized Anger Management chatbot! Our chatbot is equipped with a live webcam that will analyze your facial expressions and classify your emotions in real-time. Along with each message, please make sure to express your emotions so that our chatbot can take them into consideration during the conversation. + Our chatbot is here to provide you with a space where you can discuss your thoughts, feelings and experiences and it will respond with something that will make you angry. Our angry chatbot uses evidence-based techniques to help you get more angry, develop furious rage and raise your blood pressure. + As we talk, please feel free to share your emotions and be as open and honest as possible. Our chatbot will use its advanced emotion classifier to provide personalized ragebait and anger inducing content that is tailored to your unique needs. + Remember, our chatbot is here to make you angry and help you manage your anger. So take your time, be patient with yourself, and let's work together to improve your anger management. It will not include anything other than the message content. + """ + ] -def run_app(): # Set page title st.set_page_config(page_title="Web Camera Emotion Detector and Chatbot") + st.header("ChatGPT Wrapper + Emotion Classifier") # Set page layout col1, col2 = st.columns([2, 1]) initialize_state() + st.session_state['logger'].info(f"Starting session: [{st.session_state.guid=}]") # Add chatbox input/output display to page layout + with col2: - st.header("Live Emotion Detector") + st.subheader("Live Emotion Detector") # Initialize video feed camera_stream = webrtc_streamer( key="WYH", @@ -186,23 +287,22 @@ def run_app(): ) if camera_stream.state.playing: with col1: - st.header("AI ChatBot") + st.subheader("OpenAI ChatGPT Wrapper:") # start text input game. - st.text_input("Talk to ChatGPT Here!", key='input_text', on_change=dispatch_prompt) + st.text_input("Talk to ChatGPT Here!", key='input_text', on_change=dispatch_prompt_v2()) # Display response with st.container(): st.text('Chat History:') if len(st.session_state.chat_history) != 0: + # reversed messages for chat in st.session_state.chat_history[::-1]: - print(chat) - st_message(message=chat['message'], is_user=chat['is_user']) + # print(chat) + st_message(message=chat['message'], is_user=chat['is_user'], key=str(uuid.uuid4())) - # current_emotion_label = st.empty() json_total_emotion_display = st.empty() - # json_frame_emotion_display = st.empty() while True: if camera_stream.video_processor: @@ -212,37 +312,25 @@ def run_app(): ) # print(frame_emotion_dict) except queue.Empty: - frame_emotion_dict = {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Neutral': 0.0, 'Sad': 0.0, 'Surprise': 0.0, 'NoStrongSignal': 0} - - # # get a deep copy from facial_emotion_dict from state, += each of the values in the frame_emotion_dict, save it back to state. - # current_facial_emotion_dict = copy.deepcopy(st.session_state.facial_emotion_dict) - # for key, value in frame_emotion_dict.items(): - # current_facial_emotion_dict[key] += value - # st.session_state.facial_emotion_dict = current_facial_emotion_dict - + frame_emotion_dict = {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Neutral': 0.0, 'Sad': 0.0, 'Surprise': 0.0} current_facial_emotion_dict = copy.deepcopy(st.session_state.facial_emotion_dict) - - # if no strong signal, dont do anything - # if not all([ val<=0.15 for val in list(frame_emotion_dict.values()) ]): + if max(list(frame_emotion_dict.values())) > .20: - # get max value from emotion_dict and increment emotion in state. - # print(f"{max(list(frame_emotion_dict.values()))=}") emotion_label = max(frame_emotion_dict, key=frame_emotion_dict.get) current_facial_emotion_dict[emotion_label] += 1 st.session_state.facial_emotion_dict = current_facial_emotion_dict else: - # print(f"{max(list(frame_emotion_dict.values()))=}") - current_facial_emotion_dict['NoStrongSignal'] += 1 st.session_state.facial_emotion_dict = current_facial_emotion_dict - # current_emotion_label.text(f"Current Emotion\n{max(frame_emotion_dict, key=frame_emotion_dict.get)}: {max(frame_emotion_dict.values())}") - - # json_frame_emotion_display.json(frame_emotion_dict) json_total_emotion_display.json(current_facial_emotion_dict) else: break + if not camera_stream.state.playing: + st.session_state.system_prompt_selected = dropdown_menu("Select a system prompt for ChatGPT. Do not change it without refreshing the page.", initial_prompt_options) + st.write("ChatGPT System Prompt:", st.session_state.system_prompt_selected) + # Run the app if __name__ == '__main__': run_app() \ No newline at end of file