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