From e2d71b8fcec9743d0fbf5cb13af56508ef3d96bc Mon Sep 17 00:00:00 2001 From: Madie Laine <106930581+AdieLaine@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:19:33 -0400 Subject: [PATCH] modified: streamly.py --- streamly.py | 357 ++++++++++++++++++++++++---------------------------- 1 file changed, 167 insertions(+), 190 deletions(-) diff --git a/streamly.py b/streamly.py index 96db4c1..45bc437 100644 --- a/streamly.py +++ b/streamly.py @@ -1,35 +1,45 @@ -import logging +import openai import streamlit as st - - -from langchain.adapters import openai as lc_openai +import logging from PIL import Image, ImageEnhance import time import json import requests import base64 - - from openai import OpenAI, OpenAIError -client = OpenAI() +# Configure logging logging.basicConfig(level=logging.INFO) +# Constants +NUMBER_OF_MESSAGES_TO_DISPLAY = 20 +API_DOCS_URL = "https://docs.streamlit.io/library/api-reference" + +# Retrieve and validate API key +OPENAI_API_KEY = st.secrets.get("OPENAI_API_KEY", None) +if not OPENAI_API_KEY: + st.error("Please add your OpenAI API key to the Streamlit secrets.toml file.") + st.stop() + +# Assign OpenAI API Key +openai.api_key = OPENAI_API_KEY +client = openai.OpenAI() + # Streamlit Page Configuration st.set_page_config( page_title="Streamly - An Intelligent Streamlit Assistant", page_icon="imgs/avatar_streamly.png", layout="wide", - initial_sidebar_state="expanded", + initial_sidebar_state="auto", menu_items={ "Get help": "https://github.com/AdieLaine/Streamly", "Report a bug": "https://github.com/AdieLaine/Streamly", "About": """ ## Streamly Streamlit Assistant ### Powered using GPT-4o-mini - + **GitHub**: https://github.com/AdieLaine/ - + The AI Assistant named, Streamly, aims to provide the latest updates from Streamlit, generate code snippets for Streamlit widgets, and answer questions about Streamlit's latest features, issues, and more. @@ -38,15 +48,28 @@ } ) -# Streamlit Updates and Expanders +# Streamlit Title st.title("Streamly Streamlit Assistant") -API_DOCS_URL = "https://docs.streamlit.io/library/api-reference" +def img_to_base64(image_path): + """Convert image to base64.""" + try: + with open(image_path, "rb") as img_file: + return base64.b64encode(img_file.read()).decode() + except Exception as e: + logging.error(f"Error converting image to base64: {str(e)}") + return None @st.cache_data(show_spinner=False) def long_running_task(duration): """ Simulates a long-running operation. + + Parameters: + - duration: int, duration of the task in seconds + + Returns: + - str: Completion message """ time.sleep(duration) return "Long-running operation completed." @@ -75,184 +98,161 @@ def load_streamlit_updates(): try: with open("data/streamlit_updates.json", "r") as f: return json.load(f) - except (FileNotFoundError, json.JSONDecodeError): + except (FileNotFoundError, json.JSONDecodeError) as e: + logging.error(f"Error loading JSON: {str(e)}") return {} +def get_streamlit_api_code_version(): + """ + Get the current Streamlit API code version from the Streamlit API documentation. + + Returns: + - str: The current Streamlit API code version. + """ + try: + response = requests.get(API_DOCS_URL) + if response.status_code == 200: + return "1.36" + except requests.exceptions.RequestException as e: + logging.error(f"Error connecting to the Streamlit API documentation: {str(e)}") + return None + +def display_streamlit_updates(): + """Display the latest updates of the Streamlit.""" + with st.expander("Streamlit 1.36 Announcement", expanded=False): + st.markdown("For more details on this version, check out the [Streamlit Forum post](https://docs.streamlit.io/library/changelog#version).") + +def initialize_conversation(): + """ + Initialize the conversation history with system and assistant messages. + + Returns: + - list: Initialized conversation history. + """ + assistant_message = "Hello! I am Streamly. How can I assist you with Streamlit today?" + + conversation_history = [ + {"role": "system", "content": "You are Streamly, a specialized AI assistant trained in Streamlit."}, + {"role": "system", "content": "Streamly, is powered by the OpenAI GPT-4o-mini model, released on July 18, 2024."}, + {"role": "system", "content": "You are trained up to Streamlit Version 1.36.0, release on June 20, 2024."}, + {"role": "system", "content": "Refer to conversation history to provide context to your response."}, + {"role": "system", "content": "You were created by Madie Laine, an OpenAI Researcher."}, + {"role": "assistant", "content": assistant_message} + ] + return conversation_history + @st.cache_data(show_spinner=False) def get_latest_update_from_json(keyword, latest_updates): """ Fetch the latest Streamlit update based on a keyword. Parameters: - keyword (str): The keyword to search for in the Streamlit updates. - latest_updates (dict): The latest Streamlit updates data. + - keyword (str): The keyword to search for in the Streamlit updates. + - latest_updates (dict): The latest Streamlit updates data. Returns: - str: The latest update related to the keyword, or a message if no update is found. + - str: The latest update related to the keyword, or a message if no update is found. """ for section in ["Highlights", "Notable Changes", "Other Changes"]: for sub_key, sub_value in latest_updates.get(section, {}).items(): for key, value in sub_value.items(): if keyword.lower() in key.lower() or keyword.lower() in value.lower(): return f"Section: {section}\nSub-Category: {sub_key}\n{key}: {value}" - return "No updates found for the specified keyword." -def get_streamlit_api_code_version(): +def construct_formatted_message(latest_updates): """ - Get the current Streamlit API code version from the Streamlit API documentation. + Construct formatted message for the latest updates. + + Parameters: + - latest_updates (dict): The latest Streamlit updates data. Returns: - str: The current Streamlit API code version. + - str: Formatted update messages. """ - try: - response = requests.get(API_DOCS_URL) - if response.status_code == 200: - return "1.34.0" - except requests.exceptions.RequestException as e: - print("Error connecting to the Streamlit API documentation:", str(e)) - return None - -def display_streamlit_updates(): - """It displays the latest updates of the Streamlit.""" - with st.expander("Streamlit 1.34 Announcement", expanded=False): st.markdown("For more details on this version, check out the [Streamlit Forum post](https://docs.streamlit.io/library/changelog#version).") - -def img_to_base64(image_path): - """Convert image to base64""" - with open(image_path, "rb") as img_file: - return base64.b64encode(img_file.read()).decode() + formatted_message = [] + highlights = latest_updates.get("Highlights", {}) + version_info = highlights.get("Version 1.36", {}) + if version_info: + description = version_info.get("Description", "No description available.") + formatted_message.append(f"- **Version 1.36**: {description}") + + for category, updates in latest_updates.items(): + formatted_message.append(f"**{category}**:") + for sub_key, sub_values in updates.items(): + if sub_key != "Version 1.36": # Skip the version info as it's already included + description = sub_values.get("Description", "No description available.") + documentation = sub_values.get("Documentation", "No documentation available.") + formatted_message.append(f"- **{sub_key}**: {description}") + formatted_message.append(f" - **Documentation**: {documentation}") + return "\n".join(formatted_message) @st.cache_data(show_spinner=False) -def on_chat_submit(chat_input, api_key, latest_updates, use_langchain=False): +def on_chat_submit(chat_input, latest_updates): """ Handle chat input submissions and interact with the OpenAI API. Parameters: - chat_input (str): The chat input from the user. - api_key (str): The OpenAI API key. - latest_updates (dict): The latest Streamlit updates fetched from a JSON file or API. - use_langchain (bool): Whether to use LangChain OpenAI wrapper. + - chat_input (str): The chat input from the user. + - latest_updates (dict): The latest Streamlit updates fetched from a JSON file or API. Returns: - None: Updates the chat history in Streamlit's session state. + - None: Updates the chat history in Streamlit's session state. """ user_input = chat_input.strip().lower() - # Initialize the OpenAI API - model_engine = "gpt-4o-mini" - - # Initialize the conversation history with system and assistant messages if 'conversation_history' not in st.session_state: - assistant_message = "Hello! I am Streamly. How can I assist you with Streamlit today?" - formatted_message = [] - highlights = latest_updates.get("Highlights", {}) - - # Include version info in highlights if available - version_info = highlights.get("Version 1.34", {}) - if version_info: - description = version_info.get("Description", "No description available.") - formatted_message.append(f"- **Version 1.34**: {description}") - - for category, updates in latest_updates.items(): - formatted_message.append(f"**{category}**:") - for sub_key, sub_values in updates.items(): - if sub_key != "Version 1.34": # Skip the version info as it's already included - description = sub_values.get("Description", "No description available.") - documentation = sub_values.get("Documentation", "No documentation available.") - formatted_message.append(f"- **{sub_key}**: {description}") - formatted_message.append(f" - **Documentation**: {documentation}") - - assistant_message += "\n".join(formatted_message) - - # Initialize conversation_history - st.session_state.conversation_history = [ - {"role": "system", "content": "You are Streamly, a specialized AI assistant trained in Streamlit."}, - {"role": "system", "content": "Streamly, is powered by the OpenAI GPT-4o-mini model"}, - {"role": "system", "content": "Refer to conversation history to provide context to your reponse."}, - {"role": "system", "content": "You are trained up to Streamlit Version 1.34.0."}, - {"role": "system", "content": "You were created by Madie Laine, an OpenAI Researcher."}, - {"role": "assistant", "content": assistant_message} - ] - - - # Append user's query to conversation history + st.session_state.conversation_history = initialize_conversation() + st.session_state.conversation_history.append({"role": "user", "content": user_input}) try: - # Logic for assistant's reply + model_engine = "gpt-4o-mini" assistant_reply = "" - if use_langchain: - # LangChain OpenAI wrapper call - lc_result = lc_openai.ChatCompletion.create( - messages=st.session_state.conversation_history, + if "latest updates" in user_input: + assistant_reply = "Here are the latest highlights from Streamlit:\n" + highlights = latest_updates.get("Highlights", {}) + if highlights: + for version, info in highlights.items(): + description = info.get("Description", "No description available.") + assistant_reply += f"- **{version}**: {description}\n" + else: + assistant_reply = "No highlights found." + else: + response = client.chat.completions.create( model=model_engine, - temperature=0 + messages=st.session_state.conversation_history ) - assistant_reply = lc_result["choices"][0]["message"]["content"] + assistant_reply = response.choices[0].message.content - else: - if "latest updates" in user_input: - assistant_reply = "Here are the latest highlights from Streamlit:\n" - highlights = latest_updates.get("Highlights", {}) - if highlights: - for version, info in highlights.items(): - description = info.get("Description", "No description available.") - assistant_reply += f"- **{version}**: {description}\n" - else: - - # Direct OpenAI API call - response = client.chat.completions.create(model=model_engine, - messages=st.session_state.conversation_history) - - assistant_reply = response.choices[0].message.content - - # Append assistant's reply to the conversation history - st.session_state.conversation_history.append({"role": "assistant", "content": assistant_reply}) - - # Update the Streamlit chat history - if "history" in st.session_state: - st.session_state.history.append({"role": "user", "content": user_input}) - st.session_state.history.append({"role": "assistant", "content": assistant_reply}) - - except OpenAIError.APIConnectionError as e: + st.session_state.conversation_history.append({"role": "assistant", "content": assistant_reply}) + st.session_state.history.append({"role": "user", "content": user_input}) + st.session_state.history.append({"role": "assistant", "content": assistant_reply}) + + except OpenAIError as e: logging.error(f"Error occurred: {e}") - error_message = f"OpenAI Error: {str(e)}" - st.error(error_message) - #st.session_state.history.append({"role": "assistant", "content": error_message}) + st.error(f"OpenAI Error: {str(e)}") -def main(): - """ - Display Streamlit updates and handle the chat interface. - """ - # Initialize session state variables for chat history and conversation history +def initialize_session_state(): + """Initialize session state variables.""" if "history" not in st.session_state: st.session_state.history = [] if 'conversation_history' not in st.session_state: st.session_state.conversation_history = [] - # Initialize the chat with a greeting and Streamlit updates if the history is empty +def main(): + """ + Display Streamlit updates and handle the chat interface. + """ + initialize_session_state() + if not st.session_state.history: - latest_updates = load_streamlit_updates() # This function should be defined elsewhere to load updates - initial_bot_message = "Hello! How can I assist you with Streamlit today? Here are some of the latest highlights:\n" - updates = latest_updates.get("Highlights", {}) - if isinstance(updates, dict): # Check if updates is a dictionary - initial_bot_message = "I am Streamly, what can I help with today?" - st.session_state.history.append({"role": "assistant", "content": initial_bot_message}) - st.session_state.conversation_history = [ - {"role": "system", "content": "You are Streamly, a specialized AI assistant trained to assist with the logic and programming using Streamlit."}, - {"role": "system", "content": "Streamly, is powered by the OpenAI GPT-4o-mini model"}, - {"role": "system", "content": "You were created by Madie Laine, an OpenAI Researcher."}, - {"role": "system", "content": "Madie Laine can be found on Twitter https://twitter.com/justmadielaine or Github https://github.com/AdieLaine"}, - {"role": "system", "content": "Refer to conversation history to provide context to your reponse."}, - {"role": "system", "content": "Use the streamlit_updates.json local file to look up the latest Streamlit feature updates."}, - {"role": "system", "content": "When responding, provide code examples, links to documentation, and code examples from Streamlit API to help the user."}, - {"role": "assistant", "content": initial_bot_message} - ] - else: - st.error("Unexpected structure for 'Highlights' in latest updates.") - - # Inject custom CSS for glowing border effect + initial_bot_message = "Hello! How can I assist you with Streamlit today?" + st.session_state.history.append({"role": "assistant", "content": initial_bot_message}) + st.session_state.conversation_history = initialize_conversation() + + # Insert custom CSS for glowing effect st.markdown( """ """, unsafe_allow_html=True, ) - # Function to convert image to base64 - def img_to_base64(image_path): - with open(image_path, "rb") as img_file: - return base64.b64encode(img_file.read()).decode() - - # Load and display sidebar image with glowing effect + # Load and display sidebar image img_path = "imgs/sidebar_streamly_avatar.png" img_base64 = img_to_base64(img_path) - st.sidebar.markdown( - f'', - unsafe_allow_html=True, - ) + if img_base64: + st.sidebar.markdown( + f'', + unsafe_allow_html=True, + ) + st.sidebar.markdown("---") - + # Sidebar for Mode Selection mode = st.sidebar.radio("Select Mode:", options=["Latest Updates", "Chat with Streamly"], index=1) - use_langchain = st.sidebar.checkbox("Use LangChain OpenAI Adapter 🦜️🔗 ", value=False) + st.sidebar.markdown("---") - # Toggle checkbox in the sidebar for basic interactions - show_basic_info = st.sidebar.toggle("Show Basic Interactions", value=True) - # Display the st.info box if the checkbox is checked + # Display basic interactions + show_basic_info = st.sidebar.checkbox("Show Basic Interactions", value=True) if show_basic_info: st.sidebar.markdown(""" ### Basic Interactions @@ -307,10 +303,8 @@ def img_to_base64(image_path): - **Navigate Updates**: Switch to 'Updates' mode to browse the latest Streamlit updates in detail. """) - # Add another toggle checkbox in the sidebar for advanced interactions - show_advanced_info = st.sidebar.toggle("Show Advanced Interactions", value=False) - - # Display the st.info box if the checkbox is checked + # Display advanced interactions + show_advanced_info = st.sidebar.checkbox("Show Advanced Interactions", value=False) if show_advanced_info: st.sidebar.markdown(""" ### Advanced Interactions @@ -321,43 +315,26 @@ def img_to_base64(image_path): """) st.sidebar.markdown("---") - # Load image and convert to base64 - img_path = "imgs/stsidebarimg.png" # Replace with the actual image path - img_base64 = img_to_base64(img_path) - + # Load and display image with glowing effect + img_path = "imgs/stsidebarimg.png" + img_base64 = img_to_base64(img_path) + if img_base64: + st.sidebar.markdown( + f'', + unsafe_allow_html=True, + ) - # Display image with custom CSS class for glowing effect - st.sidebar.markdown( - f'', - unsafe_allow_html=True, - ) - - # Access API Key from st.secrets and validate it - api_key = st.secrets["OPENAI_API_KEY"] - if not api_key: - st.error("Please add your OpenAI API key to the Streamlit secrets.toml file.") - st.stop() - - # Handle Chat and Update Modes if mode == "Chat with Streamly": chat_input = st.chat_input("Ask me about Streamlit updates:") if chat_input: latest_updates = load_streamlit_updates() - on_chat_submit(chat_input, api_key, latest_updates, use_langchain) + on_chat_submit(chat_input, latest_updates) - # Display chat history with custom avatars - for message in st.session_state.history[-20:]: + # Display chat history + for message in st.session_state.history[-NUMBER_OF_MESSAGES_TO_DISPLAY:]: role = message["role"] - - # Set avatar based on role - if role == "assistant": - avatar_image = "imgs/avatar_streamly.png" - elif role == "user": - avatar_image = "imgs/stuser.png" - else: - avatar_image = None # Default - + avatar_image = "imgs/avatar_streamly.png" if role == "assistant" else "imgs/stuser.png" if role == "user" else None with st.chat_message(role, avatar=avatar_image): st.write(message["content"]) @@ -365,4 +342,4 @@ def img_to_base64(image_path): display_streamlit_updates() if __name__ == "__main__": - main() + main() \ No newline at end of file