Mimi commited on
Commit
748e8a4
·
1 Parent(s): 9dfab08

Initial commit

Browse files
Files changed (8) hide show
  1. .gitignore +132 -0
  2. .streamlit/config.toml +10 -0
  3. Dockerfile +27 -0
  4. README.md +11 -0
  5. app.py +108 -0
  6. requirements.txt +2 -0
  7. tests/__init__.py +0 -0
  8. tests/test_app.py +44 -0
.gitignore ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ pip-wheel-metadata/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # pipenv
89
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
91
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
92
+ # install all needed dependencies.
93
+ #Pipfile.lock
94
+
95
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ .vscode/
.streamlit/config.toml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [theme]
2
+ base="dark"
3
+ primaryColor="#e28198"
4
+ backgroundColor="#061222"
5
+ secondaryBackgroundColor="#8aafd8"
6
+ textColor="#fee5d7"
7
+ font="monospace"
8
+
9
+ [server]
10
+ enableStaticServing = true
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8.9
2
+
3
+ WORKDIR /app
4
+
5
+ COPY ./requirements.txt /app/requirements.txt
6
+ COPY ./packages.txt /app/packages.txt
7
+
8
+ RUN apt-get update && xargs -r -a /app/packages.txt apt-get install -y && rm -rf /var/lib/apt/lists/*
9
+ RUN pip3 install --no-cache-dir -r /app/requirements.txt
10
+
11
+ # User
12
+ RUN useradd -m -u 1000 user
13
+ USER user
14
+ ENV HOME /home/user
15
+ ENV PATH $HOME/.local/bin:$PATH
16
+
17
+ WORKDIR $HOME
18
+ RUN mkdir app
19
+ WORKDIR $HOME/app
20
+ COPY . $HOME/app
21
+
22
+ EXPOSE 8501
23
+ CMD streamlit run app.py \
24
+ --server.headless true \
25
+ --server.enableCORS false \
26
+ --server.enableXsrfProtection false \
27
+ --server.fileWatcherType none
README.md CHANGED
@@ -1,4 +1,5 @@
1
  ---
 
2
  title: Naomi
3
  emoji: 🏆
4
  colorFrom: blue
@@ -9,3 +10,13 @@ license: mit
9
  ---
10
 
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ <<<<<<< HEAD
3
  title: Naomi
4
  emoji: 🏆
5
  colorFrom: blue
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
+ =======
14
+ title: Naomi Chatbot
15
+ sdk: docker
16
+ app_port: 8501
17
+ pinned: false
18
+
19
+ ---
20
+
21
+ # naomi
22
+ >>>>>>> 47fcbb2 (Initial commit)
app.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+
3
+ app.py
4
+
5
+ Main UI script with streamlit.
6
+
7
+ Widget keys:
8
+ 'FormSubmitter:intake-Submit': form button key returns value true if clicked (bool) FormSubmitter:{form-key}-{form-submission-key}
9
+ """
10
+
11
+ import time
12
+ import random
13
+ import streamlit as st
14
+ from datetime import datetime
15
+
16
+ # Streamed response emulator
17
+ def response_generator():
18
+ response = random.choice(
19
+ [
20
+ f"Hello there! {st.session_state['candidate_name']}",
21
+ f"Hi, {st.session_state['candidate_name']}! Is there anything I can help you with?",
22
+ f"Do you need help? {st.session_state['candidate_name']}",
23
+ ]
24
+ )
25
+ for word in response.split():
26
+ yield word + " "
27
+ time.sleep(0.05)
28
+
29
+ # Title of the app
30
+ st.title("Chatbot Naomi")
31
+
32
+ print('Initial Session state', st.session_state)
33
+
34
+ contact_options = ['Instagram', 'Email', 'Number']
35
+ intake_form = [
36
+ 'candidate_name',
37
+ 'candidate_contact_type',
38
+ 'candidate_contact',
39
+ 'candidate_dob',
40
+ 'candidate_location',
41
+ 'intake_submission'
42
+ ]
43
+ datetime_format = '%Y-%m-%d %H:%M:%S'
44
+
45
+ if "messages" not in st.session_state:
46
+ st.session_state.messages = []
47
+
48
+ @st.dialog('Intake form', width='large')
49
+ def open_intake_form():
50
+ st.markdown('Fill in your detaisl below to start chat session :)')
51
+ st.session_state.candidate_name = st.text_input("Enter your name", key='candidate_name')
52
+ contact_col_1, contact_col_2 = st.columns(spec=[0.3, 0.7], vertical_alignment='center')
53
+ contact_col_1.selectbox("Select contact option", contact_options, key='candidate_contact_type')
54
+ contact_col_2.text_input('Enter your username', key='candidate_contact')
55
+ st.session_state.candidate_dob = st.date_input("When is your birthday?", key='candidate_dob')
56
+ st.session_State.candidate_location = st.text_input('Enter your location', key='candidate_location')
57
+ #button = st.button('Submit', use_container_width=True, type='primary')
58
+ # after the button is clicked the page automatically reruns and the workflow starts from the beginning
59
+
60
+ if st.button('Submit', use_container_width=True, type='primary', key='intake_submission'):
61
+ print('Session state after submission for user input: ', st.session_state)
62
+ time.sleep(1)
63
+ st.rerun()
64
+
65
+ @st.fragment()
66
+ def open_chat_window(**kwargs):
67
+ # adds to current state (deletes if doesnt)
68
+ st.session_state.update(kwargs)
69
+
70
+ st.markdown('Welcome to the chat!')
71
+ msgbox = st.container(height=400, border=False)
72
+
73
+ # Display existing chat messages
74
+ for message in st.session_state.messages:
75
+ msgbox.chat_message(message["role"]).write(message['content'])
76
+
77
+ if user_input := st.chat_input('Enter your message'):
78
+ # Add user message to chat history
79
+ print(f'State: {st.session_state}\nUser inserted message: {user_input}')
80
+
81
+ st.session_state.messages.append({"role": "user", "content": user_input, "timestamp": datetime.now()})
82
+ # Display user message in chat message container
83
+ msgbox.chat_message("user").write(user_input)
84
+ response = msgbox.chat_message('assistant').write_stream(response_generator())
85
+
86
+ # Append assistant's response to the messages history
87
+ st.session_state.messages.append({"role": "assistant", "content": response, "timestamp": datetime.now()})
88
+
89
+ undo_button, reset_button = st.columns(2)
90
+ if undo_button.button('Undo message', use_container_width=True, type='secondary'):
91
+ st.session_state['undo_button'] = True
92
+ if reset_button.button('Reset chat', use_container_width=True, type='primary'):
93
+ st.session_state.clear()
94
+ time.sleep(1)
95
+ st.rerun()
96
+
97
+ def main():
98
+ if 'intake_submission' not in st.session_state:
99
+ if st.button('Start chat session . . .', type='primary', key='open_intake'):
100
+ st.session_state.open_intake = True
101
+ open_intake_form()
102
+ else:
103
+ st.session_state['candidate_name'] = st.session_state['candidate_name'].lower().capitalize()
104
+ open_chat_window(**st.session_state)
105
+ #render_agent_mood()
106
+
107
+ if __name__ == '__main__':
108
+ main()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ streamlit
2
+ requests
tests/__init__.py ADDED
File without changes
tests/test_app.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ test_app.py
3
+
4
+ Tests the main application.
5
+
6
+ """
7
+ import warnings
8
+ import pytest
9
+ import streamlit as st
10
+ from streamlit.testing.v1 import AppTest
11
+ from app import open_intake_form
12
+
13
+ warnings.filterwarnings("ignore", message="missing ScriptRunContext")
14
+ warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*custom tp_new.*")
15
+
16
+ intake_form = [
17
+ 'candidate_name',
18
+ 'candidate_contact_type',
19
+ 'candidate_contact',
20
+ 'candidate_dob',
21
+ 'candidate_location',
22
+ 'intake_submission'
23
+ ]
24
+
25
+ @pytest.fixture
26
+ def app():
27
+ return AppTest.from_file('app.py').run()
28
+
29
+ @pytest.fixture
30
+ def form():
31
+ form = AppTest.from_function(open_intake_form).run()
32
+ form.session_state.open_intake = True
33
+
34
+ for key in intake_form:
35
+ form.session_state[key] = None
36
+ return form
37
+
38
+ def test_new_session(app):
39
+ assert 'messages' in app.session_state, f"`messages` not found in session state"
40
+
41
+ def test_open_intake_button(app):
42
+ # Simulate entering details
43
+ app.button(key='open_intake').click()
44
+ assert 'open_intake' in app.session_state