Spaces:
Sleeping
Sleeping
| # --- Importing Libraries --- | |
| import streamlit as st | |
| import config # used only for default values | |
| from openai import OpenAI | |
| from typing import Dict, Any, List | |
| import os | |
| from dotenv import load_dotenv | |
| # Load environment variables from a .env file. | |
| load_dotenv() | |
| def get_openai_api_key() -> str: | |
| api_key = os.getenv("OPENAI_API_KEY") | |
| if not api_key: | |
| raise ValueError("OpenAI API key not found. Please set it in your .env file.") | |
| return api_key | |
| def build_complete_system_prompt(selected_language: str, prompt_body: str) -> str: | |
| """ | |
| Build the complete system prompt by combining the language instruction with the prompt body. | |
| Args: | |
| selected_language (str): The language chosen from the dropdown. | |
| prompt_body (str): The editable system prompt body. | |
| Returns: | |
| str: The complete system prompt with language instruction. | |
| """ | |
| language_instruction = f"All your responses should be in {selected_language}.\n" | |
| return language_instruction + prompt_body | |
| def reset_chat_history(initial_context: Dict[str, str]) -> None: | |
| """ | |
| Reset the chat history to the initial context and rerun the app. | |
| Args: | |
| initial_context (Dict[str, str]): The initial chat context. | |
| """ | |
| st.session_state["display_messages"] = [initial_context] | |
| st.rerun() | |
| def stream_assistant_response(client: OpenAI, model: str, messages: List[Dict[str, str]], | |
| temperature: float, max_tokens: int, | |
| frequency_penalty: float, presence_penalty: float) -> str: | |
| """ | |
| Stream the assistant's response from the OpenAI API and return the full response. | |
| Args: | |
| client (OpenAI): The initialized OpenAI client. | |
| model (str): The model to use. | |
| messages (List[Dict[str, str]]): The list of chat messages. | |
| temperature (float): The temperature setting. | |
| max_tokens (int): The maximum number of tokens. | |
| frequency_penalty (float): The frequency penalty. | |
| presence_penalty (float): The presence penalty. | |
| Returns: | |
| str: The complete assistant response. | |
| """ | |
| full_response = "" | |
| message_placeholder = st.empty() | |
| try: | |
| stream = client.chat.completions.create( | |
| model=model, | |
| messages=messages, | |
| stream=True, | |
| temperature=temperature, | |
| max_tokens=max_tokens, # Use max_tokens for other models | |
| frequency_penalty=frequency_penalty, | |
| presence_penalty=presence_penalty, | |
| ) | |
| # Iterate through the stream to collect the response. | |
| for chunk in stream: | |
| if chunk.choices[0].delta.content is not None: | |
| full_response += chunk.choices[0].delta.content | |
| message_placeholder.markdown(full_response + "▌") | |
| message_placeholder.markdown(full_response) | |
| except Exception as e: | |
| st.exception(e) | |
| st.error(f"An error occurred: {str(e)}") | |
| return full_response | |
| def main() -> None: | |
| """Main function to run the Streamlit Chatbot Prompt and Parameter Tester application.""" | |
| # --- App Layout and Configuration --- | |
| st.set_page_config( | |
| layout="wide", | |
| page_title="OpenAI Chatbot Tester", | |
| page_icon=":lightbulb:", | |
| initial_sidebar_state="expanded" | |
| ) | |
| st.title("OpenAI Chatbot Tester") | |
| # --- Sidebar: Configuration Inputs --- | |
| with st.sidebar: | |
| st.header("Configuration") | |
| # Dropdown for model selection. | |
| model_options = ["gpt-4o", "gpt-4o-mini"] | |
| default_model = config.ai_model if config.ai_model in model_options else model_options[0] | |
| selected_model = st.selectbox( | |
| "Model", | |
| options=model_options, | |
| index=model_options.index(default_model), | |
| help="This controls the model used for the chatbot. You can choose from the following models: gpt-4o, gpt-4o-mini" | |
| ) | |
| st.session_state["openai_model"] = selected_model | |
| # Slider for temperature. | |
| temperature = st.slider( | |
| "Temperature", | |
| 0.0, 1.0, | |
| value=config.temperature, | |
| step=0.05, | |
| help="This controls the randomness/creativity of the responses. A higher temperature results in more creative responses." | |
| ) | |
| st.session_state["temperature"] = temperature | |
| # Number input for maximum tokens. | |
| max_tokens = st.number_input( | |
| "Max Tokens", | |
| min_value=1, | |
| max_value=2048, | |
| value=config.max_tokens, | |
| step=1, | |
| help="This controls the maximum number of tokens the AI can generate. 1000 tokens equals about 750 words." | |
| ) | |
| st.session_state["max_tokens"] = max_tokens | |
| # Slider for frequency penalty. | |
| frequency_penalty = st.slider( | |
| "Frequency Penalty", | |
| 0.0, 1.0, | |
| value=config.frequency_penalty, | |
| step=0.05, | |
| help="This controls the frequency penalty for the responses. Higher values produce more diverse responses." | |
| ) | |
| st.session_state["frequency_penalty"] = frequency_penalty | |
| # Slider for presence penalty. | |
| presence_penalty = st.slider( | |
| "Presence Penalty", | |
| 0.0, 1.0, | |
| value=config.presence_penalty, | |
| step=0.05, | |
| help="This controls the presence penalty for the responses. Higher values reduce repetition." | |
| ) | |
| st.session_state["presence_penalty"] = presence_penalty | |
| # Language selection dropdown. | |
| language_options = [ | |
| "English", "Albanian", "Amharic", "Arabic", "Armenian", "Bengali", "Bosnian", "Bulgarian", "Burmese", | |
| "Catalan", "Chinese", "Croatian", "Czech", "Danish", "Dutch", "Estonian", "Finnish", "French", | |
| "Georgian", "German", "Greek", "Gujarati", "Hindi", "Hungarian", "Icelandic", "Indonesian", | |
| "Italian", "Japanese", "Kannada", "Kazakh", "Korean", "Latvian", "Lithuanian", "Macedonian", | |
| "Malay", "Malayalam", "Marathi", "Mongolian", "Norwegian", "Persian", "Polish", "Portuguese", | |
| "Punjabi", "Romanian", "Russian", "Serbian", "Slovak", "Slovenian", "Somali", "Spanish", | |
| "Swahili", "Swedish", "Tagalog", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu", | |
| "Vietnamese" | |
| ] | |
| default_language = "English" | |
| if default_language not in language_options: | |
| language_options.insert(0, default_language) | |
| selected_language = st.selectbox( | |
| "Preferred Language", | |
| options=language_options, | |
| index=language_options.index(default_language), | |
| help="Select your preferred language for the chatbot's responses." | |
| ) | |
| st.markdown("Click the 'Update Configuration' button each time you change an option.") | |
| st.markdown("---") | |
| # --- System Instructions --- | |
| # Retrieve the editable prompt body from session state or default config. | |
| default_body = st.session_state.get("system_prompt_body", config.prompt) | |
| system_prompt_body = st.text_area( | |
| "System Instructions", | |
| value=default_body, | |
| key="system_prompt_body", | |
| help="These instructions determine the chatbot's behavior. Click 'Update Configuration' to apply changes.", | |
| height=400 | |
| ) | |
| complete_system_prompt = build_complete_system_prompt(selected_language, system_prompt_body) | |
| st.session_state["system_prompt"] = complete_system_prompt | |
| # --- Chat State Initialization --- | |
| initial_context: Dict[str, str] = { | |
| "role": "system", | |
| "content": complete_system_prompt | |
| } | |
| # --- Update System Instructions Button --- | |
| if st.button("Update Configuration"): | |
| reset_chat_history(initial_context) | |
| # Ensure the API key is provided. | |
| try: | |
| api_key = get_openai_api_key() | |
| st.session_state["OPENAI_API_KEY"] = api_key | |
| except ValueError as e: | |
| st.error(str(e)) | |
| st.stop() | |
| # Initialize the OpenAI client. | |
| client = OpenAI(api_key=st.session_state["OPENAI_API_KEY"]) | |
| # Initialize session state variables if not already set. | |
| 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"] = [initial_context] | |
| # --- Chat Input --- | |
| prompt = st.chat_input("Type your message here...") | |
| if prompt: | |
| st.session_state["display_messages"].append({"role": "user", "content": prompt}) | |
| # --- Chat Display and Response Generation --- | |
| with st.container(): | |
| # Display chat history. | |
| 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"]) | |
| # Generate the assistant's response if there is new input. | |
| if prompt: | |
| with st.chat_message("assistant"): | |
| full_response = stream_assistant_response( | |
| client=client, | |
| model=selected_model, | |
| messages=[{"role": m["role"], "content": m["content"]} | |
| for m in st.session_state["display_messages"]], | |
| temperature=temperature, | |
| max_tokens=max_tokens, | |
| frequency_penalty=frequency_penalty, | |
| presence_penalty=presence_penalty | |
| ) | |
| st.session_state["display_messages"].append( | |
| {"role": "assistant", "content": full_response} | |
| ) | |
| # --- Footer --- | |
| with st.sidebar: | |
| st.markdown("Application created by [Keefe Reuther](https://reutherlab.netlify.app/), Assistant Teaching Professor in the UC San Diego School of Biological Sciences. " | |
| "Code for this app is available [here](https://huggingface.co/spaces/keefereuther/ST_basebot) and is distributed under the [GNU GPL-3 License](https://www.gnu.org/licenses/gpl-3.0.en.html).") | |
| # --- Debug Block: Display Session State --- | |
| with st.sidebar.expander("Debug: Session State"): | |
| session_state_copy = {k: v for k, v in st.session_state.items() if k not in ["display_messages", "api_key", "OPENAI_API_KEY"]} | |
| st.json(session_state_copy) | |
| if __name__ == "__main__": | |
| main() | |