| |
| |
|
|
| import streamlit as st |
| import hmac |
| import pandas as pd |
| import random |
| import os |
| import time |
| import base64 |
| import logging |
| import io |
| import config |
| from openai import OpenAI |
|
|
| |
| |
|
|
| def check_password(): |
| """Returns `True` if the user had the correct password.""" |
|
|
| def password_entered(): |
| """Checks whether a password entered by the user is correct.""" |
| if hmac.compare_digest(st.session_state["password"], st.secrets["password"]): |
| st.session_state["password_correct"] = True |
| del st.session_state["password"] |
| else: |
| st.session_state["password_correct"] = False |
|
|
| |
| if st.session_state.get("password_correct", False): |
| return True |
|
|
| |
| st.text_input( |
| "Password", type="password", on_change=password_entered, key="password" |
| ) |
| if "password_correct" in st.session_state: |
| st.error("😕 Password incorrect") |
| return False |
|
|
| if not check_password(): |
| st.stop() |
|
|
| |
| |
|
|
| |
| st.set_page_config(layout="wide") |
|
|
| |
| df = pd.read_csv(config.default_terms_csv) |
|
|
| |
| st.title(config.app_title) |
| st.markdown(config.intro_para) |
| st.caption(config.app_author) |
| with st.expander("INSTRUCTIONS FOR STUDENTS:"): |
| st.markdown(config.instructions) |
| with st.expander("**INSTRUCTORS**: For a look at the current terms file driving the interaction, click here:"): |
| st.markdown("This is the terms.csv file that drives the interaction. You can edit this file to change the terms and context that the chatbot uses. You may add any term or phrase. You may leave the context blank if you prefer or you can add anything relevant that the GPT does not normally know about the term. This may include relevant learning objectives, course examples, notable scientists, assessment dates, syllabus information, etc.") |
| st.table(df) |
| with st.expander("**INSTRUCTORS**: For a look at the prompt driving the chatbot, click here:"): |
| st.markdown(config.display_prompt) |
| st.sidebar.title(config.sidebar_title) |
| with st.sidebar: |
| with st.expander("Click here for instructions."): |
| st.write(config.sidebar_instructions) |
|
|
| |
| |
|
|
| |
| |
| def load_terms(file_input): |
| try: |
| if isinstance(file_input, str): |
| data = pd.read_csv(file_input) |
| else: |
| data = pd.read_csv(io.StringIO(file_input.read().decode('utf-8'))) |
| return data |
| except Exception as e: |
| st.error(f"An error occurred while loading the file: {str(e)}") |
| logging.exception(f"Error loading file: {e}") |
|
|
| |
| def create_download_link(file_path, file_name): |
| try: |
| with open(file_path, "rb") as file: |
| file_content = file.read() |
| encoded_content = base64.b64encode(file_content).decode("utf-8") |
| download_link = f'<a href="data:file/csv;base64,{encoded_content}" download="{file_name}">Download {file_name}</a>' |
| return download_link |
| except FileNotFoundError: |
| error_message = f"The file {file_name} was not found." |
| st.error(error_message) |
| logging.exception(error_message) |
| except Exception as e: |
| error_message = f"An error occurred: {str(e)}" |
| st.error(error_message) |
| logging.exception(error_message) |
|
|
| |
| def get_first_column_values(df): |
| if not df.empty: |
| return df.iloc[:, 0].tolist() |
| else: |
| return [] |
|
|
| |
| template_file_path = config.default_terms_csv |
|
|
| |
| uploaded_file = st.sidebar.file_uploader(" ", type=["csv"]) |
| if uploaded_file is not None: |
| logging.info(f"File uploaded: {uploaded_file.name}") |
| st.session_state.uploaded_file = uploaded_file |
|
|
| |
| if 'uploaded_file' in st.session_state and st.session_state.uploaded_file is not None: |
| terms = load_terms(st.session_state.uploaded_file) |
| else: |
| terms = load_terms(template_file_path) |
|
|
| |
| term_list = get_first_column_values(terms) |
|
|
| st.sidebar.markdown(create_download_link(template_file_path, "terms.csv"), unsafe_allow_html=True) |
|
|
| |
| st.sidebar.markdown('<hr>', unsafe_allow_html=True) |
|
|
| |
| |
|
|
| |
| initial_context = { |
| "role": "system", |
| "content": config.initial_prompt |
| } |
|
|
| |
| if 'selected_term' not in st.session_state: |
| st.session_state.selected_term = None |
| if 'selected_context' not in st.session_state: |
| st.session_state.selected_context = None |
| if 'display_messages' not in st.session_state: |
| st.session_state.display_messages = [initial_context] |
|
|
| |
| if 'selected_term' not in st.session_state: |
| st.session_state.selected_term = None |
| if 'display_term' not in st.session_state: |
| st.session_state.display_term = False |
|
|
| |
| selected_term = st.selectbox('Select a term/question and brainstorm everything about it. This could include a definition, examples, misconceptions, associations, etc.', term_list) |
| if selected_term: |
| selected_context = terms.loc[terms['TERM'] == selected_term, 'CONTEXT'].values[0] |
| st.session_state.selected_term = selected_term |
| st.session_state.selected_context = selected_context |
| st.session_state.display_term = True |
|
|
| |
| |
| |
| updated_prompt = config.term_prompt(st.session_state.selected_term, st.session_state.selected_context, term_list) |
| initial_context = { |
| "role": "system", |
| "content": updated_prompt |
| } |
| |
| st.session_state.display_messages[0] = [initial_context] |
|
|
| |
| if st.session_state.display_term and st.session_state.selected_term: |
| st.header(st.session_state.selected_term) |
| |
| user_message = f"Define '{st.session_state.selected_term}':" |
| elif not st.session_state.selected_term: |
| st.write("") |
|
|
| |
| |
| |
| client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"]) |
|
|
| |
| if "openai_model" not in st.session_state: |
| st.session_state["openai_model"] = config.ai_model |
|
|
| if "display_messages" not in st.session_state: |
| st.session_state.display_messages[0] = [initial_context] |
|
|
| |
| if st.session_state.get('selected_term') and st.session_state.get('selected_context'): |
| updated_prompt = config.term_prompt(st.session_state.selected_term, st.session_state.selected_context, term_list) |
| initial_context = { |
| "role": "system", |
| "content": updated_prompt |
| } |
| |
| st.session_state.display_messages[0] = initial_context |
|
|
| |
| prompt = st.chat_input("Type your message here...") |
|
|
| |
| if prompt: |
| |
| if not st.session_state["display_messages"]: |
| st.session_state["display_messages"].append(initial_context) |
| st.session_state["display_messages"].append({"role": "user", "content": prompt}) |
|
|
| |
| def reset_chat_history(): |
| st.session_state["display_messages"] = [initial_context] |
| |
| if 'selected_term' in st.session_state: |
| st.session_state.selected_term = None |
| if 'selected_context' in st.session_state: |
| st.session_state.selected_context = None |
| if 'display_term' in st.session_state: |
| st.session_state.display_term = False |
| st.rerun() |
|
|
| |
| with st.container(height=400, border=True): |
| |
| for message in st.session_state["display_messages"][1:]: |
| if message["role"] == "user": |
| with st.chat_message("user"): |
| st.markdown(message["content"]) |
| else: |
| with st.chat_message("assistant"): |
| st.markdown(message["content"]) |
|
|
| |
| if prompt: |
| with st.chat_message("assistant"): |
| try: |
| stream = client.chat.completions.create( |
| model=st.session_state["openai_model"], |
| messages=[ |
| {"role": m["role"], "content": m["content"]} |
| for m in st.session_state["display_messages"] |
| ], |
| stream=True, |
| temperature=config.temperature, |
| max_tokens=config.max_tokens, |
| frequency_penalty=config.frequency_penalty, |
| presence_penalty=config.presence_penalty, |
| ) |
| response = st.write_stream(stream) |
| |
| st.session_state["display_messages"].append( |
| {"role": "assistant", "content": response} |
| ) |
| except Exception as e: |
| st.error(f"An error occurred: {str(e)}") |
|
|
| |
| if st.button("Clear Chat History"): |
| reset_chat_history() |
|
|
| st.markdown(config.warning_message, unsafe_allow_html=True) |
|
|
| |
|
|
| |
|
|
| st.sidebar.title("Resources") |
|
|
| for resource in config.resources: |
| with st.sidebar: |
| with st.sidebar: |
| with st.expander(resource["title"]): |
| st.markdown(f"Description: {resource['description']}") |
| if "url" in resource: |
| st.markdown(f"[{resource['title']}]({resource['url']})") |
| if "file_path" in resource: |
| file_path = resource["file_path"] |
| if os.path.exists(file_path): |
| with open(file_path, "rb") as file: |
| file_bytes = file.read() |
| with st.spinner(f"Loading {resource['title']}..."): |
| st.download_button( |
| label=resource["title"], |
| data=file_bytes, |
| file_name=os.path.basename(file_path), |
| mime="application/octet-stream", |
| help=resource["description"], |
| ) |
| else: |
| st.warning(f"File not found: {file_path}") |
|
|
| |
| with st.sidebar: |
| st.markdown("---") |
|
|
| st.title("About") |
|
|
| |
| st.markdown(config.app_creation_message, unsafe_allow_html=True) |
| st.markdown(config.app_repo_license_message, unsafe_allow_html=True) |
|
|