|
| 1 | +import streamlit as st |
| 2 | +from utils.config import db_credentials, MAX_TOKENS_ALLOWED, MAX_MESSAGES_TO_OPENAI, TOKEN_BUFFER |
| 3 | +from utils.system_prompts import get_final_system_prompt |
| 4 | +from utils.chat_functions import run_chat_sequence, clear_chat_history, count_tokens, prepare_sidebar_data |
| 5 | +from utils.database_functions import database_schema_dict |
| 6 | +from utils.function_calling_spec import functions |
| 7 | +from utils.helper_functions import save_conversation |
| 8 | +from assets.dark_theme import dark |
| 9 | +from assets.light_theme import light |
| 10 | +from assets.made_by_sdw import made_by_sdw |
| 11 | + |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | + |
| 16 | +if __name__ == "__main__": |
| 17 | + |
| 18 | + ########### A. SIDEBAR ########### |
| 19 | + |
| 20 | + # Prepare data for the sidebar dropdowns |
| 21 | + sidebar_data = prepare_sidebar_data(database_schema_dict) |
| 22 | + st.sidebar.markdown("<div class='made_by'>Made by SDW🔋</div>", unsafe_allow_html=True) |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | + ### POSTGRES DB OBJECTS VIEWER ### |
| 27 | + |
| 28 | + st.markdown(made_by_sdw, unsafe_allow_html=True) |
| 29 | + st.sidebar.title("🔍 Postgres DB Objects Viewer") |
| 30 | + |
| 31 | + |
| 32 | + # Dropdown for schema selection |
| 33 | + selected_schema = st.sidebar.selectbox("📂 Select a schema", list(sidebar_data.keys())) |
| 34 | + |
| 35 | + |
| 36 | + # Dropdown for table selection based on chosen Schema |
| 37 | + selected_table = st.sidebar.selectbox("📜 Select a table", list(sidebar_data[selected_schema].keys())) |
| 38 | + |
| 39 | + |
| 40 | + # Display columns of the chosen table with interactivity using checkboxes |
| 41 | + st.sidebar.subheader(f"🔗 Columns in {selected_table}") |
| 42 | + for column in sidebar_data[selected_schema][selected_table]: |
| 43 | + is_checked = st.sidebar.checkbox(f"📌 {column}") |
| 44 | + |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | + ### SAVE CONVERSATION BUTTON ### |
| 50 | + |
| 51 | + # Add a button to SAVE the chat/conversation |
| 52 | + if st.sidebar.button("Save Conversation💾"): |
| 53 | + saved_file_path = save_conversation(st.session_state["full_chat_history"]) |
| 54 | + st.sidebar.success(f"Conversation saved to: {saved_file_path}") |
| 55 | + st.sidebar.markdown(f"Conversation saved! [Open File]({saved_file_path})") |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | + |
| 61 | + ### CLEAR CONVERSATION BUTTON ### |
| 62 | + |
| 63 | + # Add a button to CLEAR the chat/conversation |
| 64 | + if st.sidebar.button("Clear Conversation🗑️"): |
| 65 | + save_conversation(st.session_state["full_chat_history"]) |
| 66 | + clear_chat_history() |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + ### TOGGLE THEME BUTTON ### |
| 73 | + |
| 74 | + # Retrieve the current theme from session state |
| 75 | + current_theme = st.session_state.get("theme", "light") |
| 76 | + st.markdown(f"<body class='{current_theme}'></body>", unsafe_allow_html=True) |
| 77 | + |
| 78 | + |
| 79 | + # Initialize the theme in session state |
| 80 | + if "theme" not in st.session_state: |
| 81 | + st.session_state.theme = "light" |
| 82 | + |
| 83 | + |
| 84 | + # Add a button to toggle the UI colour theme |
| 85 | + if st.sidebar.button("Toggle Theme🚨"): |
| 86 | + st.session_state.theme = "dark" if st.session_state.theme == "light" else "light" |
| 87 | + st.experimental_rerun() |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | + # Apply the theme based on session state |
| 92 | + theme_style = dark if st.session_state.theme == "dark" else light |
| 93 | + st.markdown(theme_style, unsafe_allow_html=True) |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | + |
| 105 | + ########### B. CHAT INTERFACE ########### |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | + ### TITLE ### |
| 110 | + |
| 111 | + # Add title to the Streamlit chatbot app |
| 112 | + st.title("🤖 AI Database Chatbot 🤓") |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | + ### SESSION STATE ### |
| 117 | + |
| 118 | + # Initialize the full chat messages history for UI |
| 119 | + if "full_chat_history" not in st.session_state: |
| 120 | + st.session_state["full_chat_history"] = [{"role": "system", "content": get_final_system_prompt(db_credentials=db_credentials)}] |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | + # Initialize the API chat messages history for OpenAI requests |
| 125 | + if "api_chat_history" not in st.session_state: |
| 126 | + st.session_state["api_chat_history"] = [{"role": "system", "content": get_final_system_prompt(db_credentials=db_credentials)}] |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | + ### CHAT FACILITATION ### |
| 131 | + |
| 132 | + # Start the chat |
| 133 | + if (prompt := st.chat_input("What do you want to know?")) is not None: |
| 134 | + st.session_state.full_chat_history.append({"role": "user", "content": prompt}) |
| 135 | + |
| 136 | + # Limit the number of messages sent to OpenAI by token count |
| 137 | + total_tokens = sum(count_tokens(message["content"]) for message in st.session_state["api_chat_history"]) |
| 138 | + while total_tokens + count_tokens(prompt) + TOKEN_BUFFER > MAX_TOKENS_ALLOWED: |
| 139 | + removed_message = st.session_state["api_chat_history"].pop(0) |
| 140 | + total_tokens -= count_tokens(removed_message["content"]) |
| 141 | + |
| 142 | + st.session_state.api_chat_history.append({"role": "user", "content": prompt}) |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | + # Display previous chat messages from full_chat_history (ingore system prompt message) |
| 147 | + for message in st.session_state["full_chat_history"][1:]: |
| 148 | + if message["role"] == "user": |
| 149 | + st.chat_message("user", avatar='🧑💻').write(message["content"]) |
| 150 | + elif message["role"] == "assistant": |
| 151 | + st.chat_message("assistant", avatar='🤖').write(message["content"]) |
| 152 | + |
| 153 | + if st.session_state["api_chat_history"][-1]["role"] != "assistant": |
| 154 | + with st.spinner("⌛Connecting to AI model..."): |
| 155 | + # Send only the most recent messages to OpenAI from api_chat_history |
| 156 | + recent_messages = st.session_state["api_chat_history"][-MAX_MESSAGES_TO_OPENAI:] |
| 157 | + new_message = run_chat_sequence(recent_messages, functions) # Get the latest message |
| 158 | + |
| 159 | + # Add this latest message to both api_chat_history and full_chat_history |
| 160 | + st.session_state["api_chat_history"].append(new_message) |
| 161 | + st.session_state["full_chat_history"].append(new_message) |
| 162 | + |
| 163 | + # Display the latest message from the assistant |
| 164 | + st.chat_message("assistant", avatar='🤖').write(new_message["content"]) |
| 165 | + |
| 166 | + max_tokens = MAX_TOKENS_ALLOWED |
| 167 | + current_tokens = sum(count_tokens(message["content"]) for message in st.session_state["full_chat_history"]) |
| 168 | + progress = min(1.0, max(0.0, current_tokens / max_tokens)) |
| 169 | + st.progress(progress) |
| 170 | + st.write(f"Tokens Used: {current_tokens}/{max_tokens}") |
| 171 | + if current_tokens > max_tokens: |
| 172 | + st.warning("Note: Due to character limits, some older messages might not be considered in ongoing conversations with the AI.") |
0 commit comments