nibas commited on
Commit
fc3ddc9
·
verified ·
1 Parent(s): 2139a5b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +211 -0
app.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from sentence_transformers import SentenceTransformer
3
+ from sklearn.metrics.pairwise import cosine_similarity
4
+ import re
5
+ from huggingface_hub import InferenceClient
6
+ import os
7
+
8
+
9
+ st.set_page_config(layout="wide")
10
+
11
+
12
+
13
+ my_initial_rag_text = f"""This is a RAG (Retrieval-Augmented Generation) chatbot application built with Streamlit that combines document context with LLM responses. Here's a breakdown of its main components:
14
+
15
+ 1. Initial Setup:
16
+ - Uses Streamlit for the web interface
17
+ - Employs SentenceTransformer for generating embeddings
18
+ - Uses HuggingFace's InferenceClient for LLM interaction
19
+ - Has a default text about a GRNET training module on LLMs
20
+
21
+ 2. State Management:
22
+ - Maintains several session state variables for:
23
+ - RAG text (the context document)
24
+ - LLM model selection
25
+ - Chat message history
26
+ - Embeddings and sentences
27
+ - HuggingFace client
28
+
29
+ 3. Interface Layout:
30
+ - Split into two columns (1:2 ratio):
31
+ - Left column: Model selection dropdown and RAG text input
32
+ - Right column: Chat interface and message history
33
+
34
+ 4. Core Functionality:
35
+ - RAG Implementation:
36
+ - Splits the context document into sentences
37
+ - When a user asks a question, it:
38
+ - Converts the question into embeddings
39
+ - Finds the 3 most relevant sentences from the context
40
+ - Adds these relevant pieces as context to the prompt
41
+
42
+ - Chat Interface:
43
+ - Streams responses from the LLM
44
+ - Maintains a chat history
45
+ - Shows message history and augmented prompts
46
+ - Uses different avatars for user and AI messages
47
+
48
+ 5. Model Options:
49
+ - Offers three LLM choices:
50
+ - Mistral-7B-Instruct-v0.3 (default)
51
+ - Qwen2.5-72B-Instruct
52
+ - Zephyr-7b-beta
53
+
54
+ The application essentially creates an intelligent chatbot that can answer questions while taking into account the context provided in the RAG text, making it particularly useful for domain-specific Q&A scenarios.
55
+
56
+ A disclaimer at the bottom reminds users about potential LLM inaccuracies and the need for verification of responses.
57
+ """
58
+
59
+ # Check if the LLM model is not already in the session state
60
+ if "my_llm_model" not in st.session_state:
61
+ # Set the default LLM model to "mistralai/Mistral-7B-Instruct-v0.3"
62
+ st.session_state["my_llm_model"] = "mistralai/Mistral-7B-Instruct-v0.3"
63
+ # Check if the SPACE_ID environment variable is not already in the session state
64
+ if "my_space" not in st.session_state:
65
+ st.session_state["my_space"] = os.environ.get("SPACE_ID")
66
+
67
+ # Function to update the LLM model client
68
+ def update_llm_model():
69
+ if st.session_state["my_space"]:
70
+ # Initialize the client with the model if SPACE_ID is available
71
+ st.session_state["client"] = InferenceClient(st.session_state["my_llm_model"])
72
+ else:
73
+ # Initialize the client with the model and token if SPACE_ID is not available
74
+ st.session_state["client"] = InferenceClient(st.session_state["my_llm_model"], token=os.getenv("HF_TOKEN"))
75
+
76
+ # Check if the client is not already in the session state
77
+ if "client" not in st.session_state:
78
+ update_llm_model()
79
+
80
+ # Check if the embeddings model is not already in the session state
81
+ if "embeddings_model" not in st.session_state:
82
+ # We will use the all-MiniLM-L6-v2 model for embeddings
83
+ st.session_state["embeddings_model"] = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
84
+
85
+ MAXIMUM_TOKENS = 512
86
+
87
+ my_system_instructions = "You are a helpful assistant. Be brief and concise. Provide your answers in 100 words or less."
88
+
89
+ first_message = "Hello, how can I help you today?"
90
+
91
+ # Check if the chat messages are not already in the session state
92
+ if "my_chat_messages" not in st.session_state:
93
+ # Initialize the chat messages list in the session state
94
+ st.session_state["my_chat_messages"] = []
95
+ # Add the system instructions to the chat messages
96
+ st.session_state["my_chat_messages"].append({"role": "system", "content": my_system_instructions})
97
+
98
+ def delete_chat_messages():
99
+ for key in st.session_state.keys():
100
+ if key != "my_rag_text":
101
+ del st.session_state[key]
102
+
103
+ augmented_prompt = ""
104
+
105
+ # Create two columns with a 1:2 ratio
106
+ column_1, column_2 = st.columns([1, 2])
107
+
108
+ # In the first column
109
+ with column_1:
110
+ # Display a disclaimer about the potential inaccuracies of Large Language Models
111
+ st.expander("Disclaimer", expanded=False).markdown("""This application and code (hereafter referred to as the 'Software') is a proof of concept at an experimental stage and is not intended to be used as a production environment. The Software is provided as is, wihtout any warranties of any kind, expressed or implied and the user assumes full responsibility for its use, implementation, and legal compliance.
112
+
113
+ The developers of the Software shall not be liable for any damages, losses, claims, or liabilities arising from the Software, including but not limited to the usage of artificial intelligence and machine learning, related errors, third-party tool failures, security breaches, intellectual property violations, legal or regulatory non-compliance, deployment risks, or any indirect, incidental, or consequential damages.
114
+
115
+ Large Language Models may provide wrong answers. Please verify the answers and comply with applicable laws and regulations.
116
+
117
+ The user agrees to indemnify and hold harmless the developers of the Software from any related claims or disputes arising from the utilization of the Software by the user.
118
+
119
+ By using the Software, you agree to the terms and conditions of the disclaimer.""")
120
+
121
+ # Add a selectbox for model selection
122
+ st.selectbox("Select the model to use:",
123
+ ["mistralai/Mistral-7B-Instruct-v0.3",
124
+ "Qwen/Qwen2.5-72B-Instruct",
125
+ "HuggingFaceH4/zephyr-7b-beta"],
126
+ key="my_llm_model", on_change=update_llm_model)
127
+
128
+ # Add a text area for RAG text input
129
+ st.text_area(label="Please enter your RAG text here:", value=my_initial_rag_text, height=500, key="my_rag_text", on_change=delete_chat_messages)
130
+
131
+ # Check if the sentences are not already in the session state
132
+ if "my_sentences" not in st.session_state:
133
+ my_sentences_split = st.session_state["my_rag_text"].split(".")
134
+ st.session_state["my_sentences"] = []
135
+ for my_sentence in my_sentences_split:
136
+ if my_sentence.strip():
137
+ st.session_state["my_sentences"].append(my_sentence.strip())
138
+
139
+ # Check if the embeddings are not already in the session state
140
+ if "my_embeddings" not in st.session_state:
141
+ st.session_state["my_embeddings"] = st.session_state["embeddings_model"].encode(st.session_state["my_sentences"])
142
+
143
+ with column_2:
144
+ # Create a container for the messages with a specified height
145
+ messages_container = st.container(height=500)
146
+
147
+ # Display the first message from the assistant
148
+ messages_container.chat_message("ai", avatar=":material/robot_2:").markdown(first_message)
149
+
150
+ # Iterate through the chat messages stored in the session state
151
+ for message in st.session_state["my_chat_messages"]:
152
+ if message["role"] == "user":
153
+ # Display user messages with a specific avatar - https://fonts.google.com/icons
154
+ messages_container.chat_message(message["role"], avatar=":material/psychology_alt:").markdown(message["content"])
155
+ elif message["role"] == "assistant":
156
+ # Display assistant messages with a specific avatar
157
+ messages_container.chat_message(message["role"], avatar=":material/robot_2:").markdown(message["content"])
158
+
159
+ # Check if there is a new prompt from the user
160
+ if prompt := st.chat_input("you may ask here your questions"):
161
+
162
+ # Encode the user's prompt to get its embedding
163
+ my_question_embedding = st.session_state.embeddings_model.encode([prompt])
164
+
165
+ # Calculate the cosine similarity between the prompt embedding and stored embeddings
166
+ similarity_to_question = cosine_similarity(my_question_embedding, st.session_state.my_embeddings).flatten()
167
+
168
+ # Number of sentences to keep based on similarity
169
+ nof_keep_sentences = 3
170
+
171
+ # Get the indices of the top similar sentences
172
+ sorted_indices = similarity_to_question.argsort()[::-1][:nof_keep_sentences]
173
+
174
+ # Retrieve the top similar sentences
175
+ sorted_sentences = [st.session_state.my_sentences[i] for i in sorted_indices]
176
+
177
+ # Construct the augmented prompt with the similar sentences
178
+ augmented_prompt = "Here is the context:"
179
+ for sentence in sorted_sentences:
180
+ augmented_prompt += "\n\n" + 20*"-" + f"\n\n{sentence}"
181
+ augmented_prompt += "\n\n" + 20*"-" + "\n\n" + "The user said:" + f"\n\n{prompt}"
182
+
183
+ # Display the user's prompt in the chat container with a specific avatar
184
+ messages_container.chat_message("user", avatar=":material/psychology_alt:").markdown(prompt)
185
+ # Append the augmented prompt to the chat messages in the session state
186
+ st.session_state["my_chat_messages"].append({"role": "user", "content": augmented_prompt})
187
+ # Create an empty container for the streaming response from the assistant
188
+ with messages_container.chat_message("ai", avatar=":material/robot_2:"):
189
+ response_placeholder = st.empty()
190
+ response = ""
191
+ # Stream the response from the assistant and update the placeholder
192
+ for chunk in st.session_state["client"].chat.completions.create(messages=st.session_state["my_chat_messages"], stream=True, max_tokens=512):
193
+ if chunk.choices[0].delta.content:
194
+ response += chunk.choices[0].delta.content
195
+ # Use markdown to update the response placeholder with the streamed content
196
+ response_placeholder.markdown(response)
197
+
198
+ # Remove the last message from the chat messages in the session state
199
+ st.session_state["my_chat_messages"].pop()
200
+ # Append the user's original prompt to the chat messages in the session state
201
+ st.session_state["my_chat_messages"].append({"role": "user", "content": prompt})
202
+ # Append the assistant's response to the chat messages in the session state
203
+ st.session_state["my_chat_messages"].append({"role": "assistant", "content": response})
204
+
205
+
206
+ # Display the chat messages history
207
+ st.write("Messages History:")
208
+ st.json(st.session_state["my_chat_messages"], expanded=False)
209
+ # Display the augmented prompt used for generating the response
210
+ st.write("Augmented prompt:")
211
+ st.json({"augmented_prompt": augmented_prompt}, expanded=False)