patronmoses commited on
Commit
e26f9a8
·
verified ·
1 Parent(s): d72f0d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -234
app.py CHANGED
@@ -1,234 +1,61 @@
1
- # app.py
2
-
3
- import streamlit as st
4
- import os
5
- import faiss
6
- import pickle
7
- from sentence_transformers import SentenceTransformer
8
- from groq import Groq
9
- from dotenv import load_dotenv
10
- import re # Import regular expressions for expand_query_with_llm_app
11
-
12
- # --- Configuration and Model Loading (best outside functions to use caching) ---
13
- @st.cache_resource # Important for caching large models and data
14
- def load_models_and_data():
15
- # Load environment variables (if .env file is present in the Space)
16
- load_dotenv()
17
- groq_api_key_app = os.getenv("GROQ_API_KEY") # Ensure the key is available in the Space (see Step 3 of deployment)
18
-
19
- # Paths to the index and chunks
20
- output_folder = "faiss_index_bits" # Must be present in the HF Space
21
- index_path = os.path.join(output_folder, "bits_tutor.index")
22
- chunks_path = os.path.join(output_folder, "bits_chunks.pkl")
23
-
24
- # Load FAISS Index
25
- if not os.path.exists(index_path):
26
- st.error(f"FAISS Index not found at: {index_path}")
27
- return None, None, None, None
28
- index_loaded = faiss.read_index(index_path)
29
-
30
- # Load Chunks
31
- if not os.path.exists(chunks_path):
32
- st.error(f"Chunks file not found at: {chunks_path}")
33
- return None, None, None, None
34
- with open(chunks_path, "rb") as f:
35
- chunks_loaded = pickle.load(f)
36
-
37
- # Load Embedding Model
38
- embedding_model_name_app = "Sahajtomar/German-semantic"
39
- embedding_model_loaded = SentenceTransformer(embedding_model_name_app)
40
-
41
- # Initialize Groq Client
42
- if not groq_api_key_app:
43
- st.error("GROQ_API_KEY not found. Please add it as a Secret in the Hugging Face Space settings.")
44
- return None, None, None, None
45
- groq_client_loaded = Groq(api_key=groq_api_key_app)
46
-
47
- return index_loaded, chunks_loaded, embedding_model_loaded, groq_client_loaded
48
-
49
- # Load models and data when the app starts
50
- faiss_index, chunks_data, embedding_model, groq_client = load_models_and_data()
51
-
52
- # --- Functions from your notebook (slightly adapted for Streamlit) ---
53
-
54
- # retrieve_relevant_chunks (adapted for app)
55
- def retrieve_relevant_chunks_app(query, k=5):
56
- if embedding_model is None or faiss_index is None or chunks_data is None:
57
- return []
58
- query_embedding = embedding_model.encode([query], convert_to_numpy=True)
59
- distances, indices = faiss_index.search(query_embedding, k)
60
- retrieved_chunks_data = [(chunks_data[i], distances[0][j]) for j, i in enumerate(indices[0])]
61
- return retrieved_chunks_data
62
-
63
- # generate_answer (adapted for app - prompt remains German for German tutor functionality)
64
- def generate_answer_app(query, retrieved_chunks_data):
65
- if groq_client is None:
66
- return "Groq Client not initialized." # Developer-facing error
67
- context = "\n\n".join([chunk_text for chunk_text, dist in retrieved_chunks_data])
68
- # This prompt_template remains in German as it instructs the LLM for the German-speaking tutor
69
- prompt_template = f"""Beantworte die folgende Frage ausschliesslich basierend auf dem bereitgestellten Kontext aus den Lehrmaterialien zur Business IT Strategie.
70
- Antworte auf Deutsch.
71
-
72
- Kontext:
73
- {context}
74
-
75
- Frage: {query}
76
-
77
- Antwort:
78
- """
79
- try:
80
- chat_completion = groq_client.chat.completions.create(
81
- messages=[{"role": "user", "content": prompt_template}],
82
- model="llama3-70b-8192",
83
- temperature=0.3,
84
- )
85
- return chat_completion.choices[0].message.content
86
- except Exception as e:
87
- # Developer-facing error
88
- return f"Error during LLM request: {e}"
89
-
90
- # expand_query_with_llm (adapted for app - prompt for expansion remains German)
91
- def expand_query_with_llm_app(original_query, llm_client_app):
92
- """
93
- Expands a given user query using an LLM
94
- to generate alternative formulations or relevant keywords.
95
- Cleans the LLM's output.
96
- (This function needs to use the llm_client_app passed to it)
97
- """
98
- if llm_client_app is None:
99
- st.warning("LLM client for query expansion not initialized.")
100
- return [original_query]
101
-
102
- # This prompt_template remains in German as it instructs the LLM for the German-speaking tutor's expansion
103
- prompt_template_expansion = f"""Gegeben ist die folgende Nutzerfrage zum Thema "Business IT Strategie": "{original_query}"
104
-
105
- Bitte generiere 2-3 alternative Formulierungen dieser Frage ODER eine Liste von 3-5 sehr relevanten Schlüsselbegriffen/Konzepten,
106
- die helfen würden, in einer Wissensdatenbank nach Antworten zu dieser Frage zu suchen.
107
- Formatiere die Ausgabe klar, z.B. als nummerierte Liste für alternative Fragen oder als kommaseparierte Liste für Schlüsselbegriffe.
108
- Gib NUR die alternativen Formulierungen oder die Schlüsselbegriffe aus. Keine Einleitungssätze.
109
- """
110
- try:
111
- chat_completion = llm_client_app.chat.completions.create(
112
- messages=[
113
- {
114
- "role": "user",
115
- "content": prompt_template_expansion,
116
- }
117
- ],
118
- model="llama3-8b-8192",
119
- temperature=0.5,
120
- )
121
- expanded_terms_text = chat_completion.choices[0].message.content
122
-
123
- cleaned_queries = []
124
- potential_queries = expanded_terms_text.split('\n')
125
-
126
- for line in potential_queries:
127
- line = line.strip()
128
- line = re.sub(r"^\s*\d+\.\s*", "", line)
129
- line = re.sub(r"^\s*[-\*]\s*", "", line)
130
- line = line.strip()
131
-
132
- if not line or \
133
- line.lower().startswith("here are") or \
134
- line.lower().startswith("sicher, hier sind") or \
135
- line.lower().startswith("alternative formulierungen:") or \
136
- line.lower().startswith("*alternative formulierungen:**") or \
137
- len(line) < 5:
138
- continue
139
- cleaned_queries.append(line)
140
-
141
- if len(cleaned_queries) == 1 and ',' in cleaned_queries[0] and len(cleaned_queries[0].split(',')) > 1:
142
- final_expanded_list = [term.strip() for term in cleaned_queries[0].split(',') if term.strip() and len(term.strip()) > 4]
143
- else:
144
- final_expanded_list = cleaned_queries
145
-
146
- all_queries = [original_query]
147
- for q_exp in final_expanded_list:
148
- is_duplicate = False
149
- for q_all in all_queries:
150
- if q_all.lower() == q_exp.lower():
151
- is_duplicate = True
152
- break
153
- if not is_duplicate:
154
- all_queries.append(q_exp)
155
-
156
- return all_queries[:4]
157
-
158
- except Exception as e:
159
- st.warning(f"Error during Query Expansion with LLM: {e}")
160
- return [original_query]
161
-
162
- def retrieve_with_expanded_queries_app(original_query, llm_client_app, retrieve_func, k_per_expansion=2):
163
- """
164
- Performs Query Expansion and retrieves chunks for each expanded query.
165
- Collects and de-duplicates the chunks.
166
- (This function needs to use the llm_client_app passed to it)
167
- """
168
- expanded_queries = expand_query_with_llm_app(original_query, llm_client_app)
169
- # st.write(f"Using the following queries for retrieval after expansion:") # For debugging
170
- # for i, eq_query in enumerate(expanded_queries):
171
- # st.caption(f" ExpQuery {i}: {eq_query}")
172
-
173
- all_retrieved_chunks_data = []
174
- for eq_query in expanded_queries:
175
- retrieved_for_eq = retrieve_func(eq_query, k=k_per_expansion)
176
- all_retrieved_chunks_data.extend(retrieved_for_eq)
177
-
178
- unique_chunks_dict = {}
179
- for chunk_text, distance in all_retrieved_chunks_data:
180
- if chunk_text not in unique_chunks_dict or distance < unique_chunks_dict[chunk_text]:
181
- unique_chunks_dict[chunk_text] = distance
182
-
183
- sorted_unique_chunks_data = sorted(unique_chunks_dict.items(), key=lambda item: item[1])
184
-
185
- final_chunks_for_context = sorted_unique_chunks_data[:5]
186
- # st.write(f"\n{len(final_chunks_for_context)} unique chunks were selected for the context.") # For debugging
187
- return final_chunks_for_context
188
-
189
- # --- Streamlit UI ---
190
- st.set_page_config(page_title="RAG BITS Tutor", page_icon="🎓") # Set page title and icon
191
-
192
- st.title("🎓 RAG Study Tutor for Business IT Strategy")
193
- st.write("Ask your questions about the content of the lecture notes and case studies (in German).")
194
-
195
- # User query input field (remains German for the user)
196
- user_query_streamlit = st.text_input("Deine Frage:", "")
197
-
198
- # Option to use query expansion
199
- use_expansion = st.checkbox("Use Query Expansion (may improve results for some questions)", value=True) # Default to True
200
-
201
- if user_query_streamlit:
202
- if faiss_index and chunks_data and embedding_model and groq_client:
203
- st.write("Searching for relevant information...")
204
-
205
- if use_expansion:
206
- st.caption("Query expansion is active...")
207
- retrieved_chunks = retrieve_with_expanded_queries_app(user_query_streamlit, groq_client, retrieve_relevant_chunks_app, k_per_expansion=2)
208
- else:
209
- st.caption("Direct retrieval...")
210
- retrieved_chunks = retrieve_relevant_chunks_app(user_query_streamlit, k=3) # Number of chunks to retrieve
211
-
212
- if retrieved_chunks:
213
- # Optional display of retrieved context snippets (for debugging or transparency)
214
- # with st.expander("Show retrieved context snippets"):
215
- # for i, (chunk, dist) in enumerate(retrieved_chunks):
216
- # st.caption(f"Chunk {i+1} (Distance: {dist:.2f})")
217
- # st.markdown(f"_{chunk[:200]}..._") # Displaying German chunk
218
- # st.divider()
219
-
220
- st.write("Generating answer...")
221
- answer = generate_answer_app(user_query_streamlit, retrieved_chunks)
222
- st.subheader("Tutor's Answer:") # UI element
223
- st.markdown(answer) # Displaying German answer
224
- else:
225
- st.warning("No relevant information could be found for your query.") # UI element
226
- else:
227
- st.error("The application could not be initialized correctly. Please check the error messages above.") # UI element
228
-
229
- st.sidebar.header("About this Project") # UI element
230
- st.sidebar.info( # UI element
231
- "This RAG application was developed as part of the 'AI Applications' module. "
232
- "It uses Sentence Transformers for embeddings, FAISS for vector search, "
233
- "and an LLM via Groq for answer generation."
234
- )
 
1
+ # app.py
2
+
3
+ import streamlit as st
4
+ import os
5
+ import faiss
6
+ import pickle
7
+ from sentence_transformers import SentenceTransformer
8
+ from groq import Groq
9
+ from dotenv import load_dotenv
10
+ import re # Import regular expressions for expand_query_with_llm_app
11
+
12
+ # --- Page Configuration (MUST BE THE FIRST STREAMLIT COMMAND) ---
13
+ st.set_page_config(page_title="RAG BITS Tutor", page_icon="🎓") # Set page title and icon
14
+
15
+ # --- Konfiguration und Modell-Laden (am besten ausserhalb von Funktionen, um Caching zu nutzen) ---
16
+ @st.cache_resource # Wichtig für das Caching von grossen Modellen und Daten
17
+ def load_models_and_data():
18
+ # Lade Umgebungsvariablen (falls .env Datei im Space vorhanden ist)
19
+ load_dotenv()
20
+ groq_api_key_app = os.getenv("GROQ_API_KEY") # Stelle sicher, dass der Key im Space verfügbar ist (siehe Schritt 3)
21
+
22
+ # Pfade zum Index und den Chunks
23
+ output_folder = "faiss_index_bits" # Muss im HF Space vorhanden sein
24
+ index_path = os.path.join(output_folder, "bits_tutor.index")
25
+ chunks_path = os.path.join(output_folder, "bits_chunks.pkl")
26
+
27
+ # Lade FAISS Index
28
+ if not os.path.exists(index_path):
29
+ st.error(f"FAISS Index nicht gefunden unter: {index_path}") # This is a Streamlit command
30
+ return None, None, None, None
31
+ index_loaded = faiss.read_index(index_path)
32
+
33
+ # Lade Chunks
34
+ if not os.path.exists(chunks_path):
35
+ st.error(f"Chunks-Datei nicht gefunden unter: {chunks_path}") # This is a Streamlit command
36
+ return None, None, None, None
37
+ with open(chunks_path, "rb") as f:
38
+ chunks_loaded = pickle.load(f)
39
+
40
+ # Lade Embedding-Modell
41
+ embedding_model_name_app = "Sahajtomar/German-semantic"
42
+ embedding_model_loaded = SentenceTransformer(embedding_model_name_app)
43
+
44
+ # Initialisiere Groq Client
45
+ if not groq_api_key_app:
46
+ st.error("GROQ_API_KEY nicht gefunden. Bitte im Hugging Face Space als Secret hinzufügen.") # This is a Streamlit command
47
+ return None, None, None, None
48
+ groq_client_loaded = Groq(api_key=groq_api_key_app)
49
+
50
+ return index_loaded, chunks_loaded, embedding_model_loaded, groq_client_loaded
51
+
52
+ # Lade Modelle und Daten beim Start der App
53
+ # Wichtig: Die Funktion load_models_and_data() verwendet st.error(), was ein Streamlit-Befehl ist.
54
+ # Daher muss st.set_page_config() VOR dem ersten möglichen Aufruf von st.error() stehen.
55
+ faiss_index, chunks_data, embedding_model, groq_client = load_models_and_data()
56
+
57
+ # ... (Rest deines app.py Skripts bleibt gleich) ...
58
+
59
+ # --- Streamlit UI (kommt nach load_models_and_data) ---
60
+ st.title("🎓 RAG Study Tutor for Business IT Strategy")
61
+ # ... etc. ...