37-AN commited on
Commit
9f0d171
·
1 Parent(s): 403ced7

Fix 403 error by using local models

Browse files
Files changed (5) hide show
  1. Dockerfile +13 -4
  2. app.py +40 -1
  3. app/config.py +23 -9
  4. app/core/memory.py +84 -19
  5. app/ui/streamlit_app.py +99 -35
Dockerfile CHANGED
@@ -27,9 +27,12 @@ RUN mkdir -p /app/models && chmod 777 /app/models
27
  # Copy the rest of the application
28
  COPY . .
29
 
30
- # Create necessary directories with proper permissions
31
- RUN mkdir -p data/documents data/vector_db && \
32
- chmod -R 777 data
 
 
 
33
 
34
  # Set environment variables for cache locations
35
  ENV TRANSFORMERS_CACHE=/app/models
@@ -51,8 +54,14 @@ ENV MAX_TOKENS=256
51
  ENV CHUNK_SIZE=512
52
  ENV CHUNK_OVERLAP=128
53
 
 
 
 
 
 
 
54
  # Expose port for Hugging Face Spaces
55
  EXPOSE 7860
56
 
57
  # Run the Streamlit app on the correct port
58
- CMD ["streamlit", "run", "app/ui/streamlit_app.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
27
  # Copy the rest of the application
28
  COPY . .
29
 
30
+ # Create necessary directories with proper permissions and unique vector_db folders
31
+ RUN mkdir -p data/documents && chmod -R 777 data/documents
32
+ RUN mkdir -p data/vector_db && chmod -R 777 data/vector_db
33
+ # Create multiple vector_db instances to avoid collisions
34
+ RUN mkdir -p data/vector_db_1 data/vector_db_2 data/vector_db_3 && \
35
+ chmod -R 777 data/vector_db_*
36
 
37
  # Set environment variables for cache locations
38
  ENV TRANSFORMERS_CACHE=/app/models
 
54
  ENV CHUNK_SIZE=512
55
  ENV CHUNK_OVERLAP=128
56
 
57
+ # Set server.maxMessageSize for Streamlit to handle large uploads
58
+ ENV STREAMLIT_SERVER_MAX_MESSAGE_SIZE=200
59
+
60
+ # Set shared memory settings to improve performance
61
+ ENV PYTHONHASHSEED=0
62
+
63
  # Expose port for Hugging Face Spaces
64
  EXPOSE 7860
65
 
66
  # Run the Streamlit app on the correct port
67
+ CMD ["streamlit", "run", "app/ui/streamlit_app.py", "--server.port=7860", "--server.address=0.0.0.0", "--server.maxUploadSize=10"]
app.py CHANGED
@@ -6,6 +6,20 @@ This file starts the Streamlit UI when deployed to Hugging Face Spaces.
6
  import subprocess
7
  import os
8
  import sys
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # Make sure the app directory is in the path
11
  # Add the current directory to the path so that 'app' is recognized as a package
@@ -14,11 +28,36 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
14
  sys.path.append(os.path.abspath('.'))
15
 
16
  # Create necessary directories
 
17
  os.makedirs('data/documents', exist_ok=True)
18
  os.makedirs('data/vector_db', exist_ok=True)
19
 
 
 
 
 
 
 
 
 
 
 
20
  # Set environment variable for Python path
21
  os.environ['PYTHONPATH'] = os.path.abspath('.')
22
 
 
 
 
 
23
  # Run the Streamlit app with specific port to match huggingface-space.yml
24
- subprocess.run(["streamlit", "run", "app/ui/streamlit_app.py", "--server.port=7860", "--server.address=0.0.0.0"])
 
 
 
 
 
 
 
 
 
 
 
6
  import subprocess
7
  import os
8
  import sys
9
+ import time
10
+ import random
11
+ import logging
12
+
13
+ # Configure logging
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s [%(levelname)s] %(message)s",
17
+ handlers=[
18
+ logging.StreamHandler()
19
+ ]
20
+ )
21
+
22
+ logger = logging.getLogger(__name__)
23
 
24
  # Make sure the app directory is in the path
25
  # Add the current directory to the path so that 'app' is recognized as a package
 
28
  sys.path.append(os.path.abspath('.'))
29
 
30
  # Create necessary directories
31
+ logger.info("Creating necessary directories...")
32
  os.makedirs('data/documents', exist_ok=True)
33
  os.makedirs('data/vector_db', exist_ok=True)
34
 
35
+ # Create multiple vector database paths to help with concurrent access
36
+ for i in range(1, 4):
37
+ path = f'data/vector_db_{i}'
38
+ os.makedirs(path, exist_ok=True)
39
+ # Ensure directories have proper permissions
40
+ try:
41
+ os.chmod(path, 0o777)
42
+ except Exception as e:
43
+ logger.warning(f"Could not set permissions for {path}: {e}")
44
+
45
  # Set environment variable for Python path
46
  os.environ['PYTHONPATH'] = os.path.abspath('.')
47
 
48
+ # Add a small delay to ensure directory creation is complete
49
+ logger.info("Starting application...")
50
+ time.sleep(1)
51
+
52
  # Run the Streamlit app with specific port to match huggingface-space.yml
53
+ # Add server.maxMessageSize to handle larger files and messages
54
+ cmd = [
55
+ "streamlit", "run", "app/ui/streamlit_app.py",
56
+ "--server.port=7860",
57
+ "--server.address=0.0.0.0",
58
+ "--server.maxUploadSize=10",
59
+ "--server.maxMessageSize=200"
60
+ ]
61
+
62
+ logger.info(f"Running command: {' '.join(cmd)}")
63
+ subprocess.run(cmd)
app/config.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  from dotenv import load_dotenv
3
  from pathlib import Path
4
 
@@ -10,18 +11,31 @@ load_dotenv(dotenv_path=env_path)
10
  HF_API_KEY = os.getenv('HF_API_KEY', '')
11
 
12
  # LLM Configuration
13
- LLM_MODEL = os.getenv('LLM_MODEL', 'google/flan-t5-large')
14
  EMBEDDING_MODEL = os.getenv('EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2')
15
 
16
  # Vector Database
17
- VECTOR_DB_PATH = os.getenv('VECTOR_DB_PATH', './data/vector_db')
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  COLLECTION_NAME = os.getenv('COLLECTION_NAME', 'personal_assistant')
19
 
20
  # Application Settings
21
  DEFAULT_TEMPERATURE = float(os.getenv('DEFAULT_TEMPERATURE', 0.7))
22
- CHUNK_SIZE = int(os.getenv('CHUNK_SIZE', 1000))
23
- CHUNK_OVERLAP = int(os.getenv('CHUNK_OVERLAP', 200))
24
- MAX_TOKENS = int(os.getenv('MAX_TOKENS', 512))
25
 
26
  # Create a template .env file if it doesn't exist
27
  def create_env_example():
@@ -31,7 +45,7 @@ def create_env_example():
31
  HF_API_KEY=your_huggingface_api_key_here
32
 
33
  # LLM Configuration
34
- LLM_MODEL=google/flan-t5-large # Free model with good performance
35
  EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
36
 
37
  # Vector Database
@@ -40,7 +54,7 @@ COLLECTION_NAME=personal_assistant
40
 
41
  # Application Settings
42
  DEFAULT_TEMPERATURE=0.7
43
- CHUNK_SIZE=1000
44
- CHUNK_OVERLAP=200
45
- MAX_TOKENS=512
46
  """)
 
1
  import os
2
+ import random
3
  from dotenv import load_dotenv
4
  from pathlib import Path
5
 
 
11
  HF_API_KEY = os.getenv('HF_API_KEY', '')
12
 
13
  # LLM Configuration
14
+ LLM_MODEL = os.getenv('LLM_MODEL', 'distilgpt2')
15
  EMBEDDING_MODEL = os.getenv('EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2')
16
 
17
  # Vector Database
18
+ # Determine which vector DB path to use based on deployment environment
19
+ if os.path.exists("/app/data/vector_db_1"):
20
+ # We're in the Docker container, use one of the multiple DB paths
21
+ vector_db_options = [
22
+ './data/vector_db_1',
23
+ './data/vector_db_2',
24
+ './data/vector_db_3',
25
+ ]
26
+ # Choose a random DB path to reduce collision probability
27
+ VECTOR_DB_PATH = os.getenv('VECTOR_DB_PATH', random.choice(vector_db_options))
28
+ else:
29
+ # Local development, use the standard path
30
+ VECTOR_DB_PATH = os.getenv('VECTOR_DB_PATH', './data/vector_db')
31
+
32
  COLLECTION_NAME = os.getenv('COLLECTION_NAME', 'personal_assistant')
33
 
34
  # Application Settings
35
  DEFAULT_TEMPERATURE = float(os.getenv('DEFAULT_TEMPERATURE', 0.7))
36
+ CHUNK_SIZE = int(os.getenv('CHUNK_SIZE', 512))
37
+ CHUNK_OVERLAP = int(os.getenv('CHUNK_OVERLAP', 128))
38
+ MAX_TOKENS = int(os.getenv('MAX_TOKENS', 256))
39
 
40
  # Create a template .env file if it doesn't exist
41
  def create_env_example():
 
45
  HF_API_KEY=your_huggingface_api_key_here
46
 
47
  # LLM Configuration
48
+ LLM_MODEL=distilgpt2 # Use small model for Hugging Face Spaces
49
  EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
50
 
51
  # Vector Database
 
54
 
55
  # Application Settings
56
  DEFAULT_TEMPERATURE=0.7
57
+ CHUNK_SIZE=512
58
+ CHUNK_OVERLAP=128
59
+ MAX_TOKENS=256
60
  """)
app/core/memory.py CHANGED
@@ -1,11 +1,18 @@
1
  import os
2
  import sys
 
 
 
3
  from langchain.vectorstores import Qdrant
4
  from langchain.chains import ConversationalRetrievalChain
5
  from langchain.memory import ConversationBufferMemory
6
  from qdrant_client import QdrantClient
7
  from qdrant_client.models import Distance, VectorParams
8
 
 
 
 
 
9
  # Add project root to path for imports
10
  sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
11
  from app.config import VECTOR_DB_PATH, COLLECTION_NAME
@@ -26,29 +33,79 @@ class MemoryManager:
26
  )
27
 
28
  def _init_qdrant_client(self):
29
- """Initialize the Qdrant client."""
 
30
  os.makedirs(VECTOR_DB_PATH, exist_ok=True)
31
- return QdrantClient(path=VECTOR_DB_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def _init_vector_store(self):
34
  """Initialize the vector store."""
35
- collections = self.client.get_collections().collections
36
- collection_names = [collection.name for collection in collections]
37
-
38
- # Get vector dimension from the embedding model
39
- vector_size = len(self.embeddings.embed_query("test"))
40
-
41
- if COLLECTION_NAME not in collection_names:
42
- self.client.create_collection(
 
 
 
 
 
 
 
 
 
43
  collection_name=COLLECTION_NAME,
44
- vectors_config=VectorParams(size=vector_size, distance=Distance.COSINE),
 
 
 
 
 
 
 
 
 
 
45
  )
46
-
47
- return Qdrant(
48
- client=self.client,
49
- collection_name=COLLECTION_NAME,
50
- embeddings=self.embeddings
51
- )
52
 
53
  def get_retriever(self):
54
  """Get the retriever for RAG."""
@@ -69,8 +126,16 @@ class MemoryManager:
69
 
70
  def add_texts(self, texts, metadatas=None):
71
  """Add texts to the vector store."""
72
- return self.vectorstore.add_texts(texts=texts, metadatas=metadatas)
 
 
 
 
73
 
74
  def similarity_search(self, query, k=5):
75
  """Perform a similarity search."""
76
- return self.vectorstore.similarity_search(query, k=k)
 
 
 
 
 
1
  import os
2
  import sys
3
+ import time
4
+ import random
5
+ import logging
6
  from langchain.vectorstores import Qdrant
7
  from langchain.chains import ConversationalRetrievalChain
8
  from langchain.memory import ConversationBufferMemory
9
  from qdrant_client import QdrantClient
10
  from qdrant_client.models import Distance, VectorParams
11
 
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
  # Add project root to path for imports
17
  sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
18
  from app.config import VECTOR_DB_PATH, COLLECTION_NAME
 
33
  )
34
 
35
  def _init_qdrant_client(self):
36
+ """Initialize the Qdrant client with retry logic for concurrent access issues."""
37
+ # Create directory if it doesn't exist
38
  os.makedirs(VECTOR_DB_PATH, exist_ok=True)
39
+
40
+ # Add a small random delay to reduce chance of concurrent access
41
+ time.sleep(random.uniform(0.1, 0.5))
42
+
43
+ # Generate a unique path for this instance to avoid collision
44
+ instance_id = str(random.randint(10000, 99999))
45
+ unique_path = os.path.join(VECTOR_DB_PATH, f"instance_{instance_id}")
46
+
47
+ max_retries = 3
48
+ retry_count = 0
49
+
50
+ while retry_count < max_retries:
51
+ try:
52
+ logger.info(f"Attempting to initialize Qdrant client (attempt {retry_count+1}/{max_retries})")
53
+ # Try to use the unique path first
54
+ try:
55
+ os.makedirs(unique_path, exist_ok=True)
56
+ return QdrantClient(path=unique_path)
57
+ except Exception as e:
58
+ logger.warning(f"Could not use unique path {unique_path}: {e}")
59
+
60
+ # Try the main path as fallback
61
+ return QdrantClient(path=VECTOR_DB_PATH)
62
+
63
+ except RuntimeError as e:
64
+ if "already accessed by another instance" in str(e):
65
+ retry_count += 1
66
+ wait_time = random.uniform(0.5, 2.0) * retry_count
67
+ logger.warning(f"Qdrant concurrent access detected. Retrying in {wait_time:.2f} seconds...")
68
+ time.sleep(wait_time)
69
+ else:
70
+ # Different error, don't retry
71
+ raise
72
+
73
+ # If all retries failed, try to use in-memory storage as last resort
74
+ logger.warning("All Qdrant client initialization attempts failed. Using in-memory mode.")
75
+ return QdrantClient(":memory:")
76
 
77
  def _init_vector_store(self):
78
  """Initialize the vector store."""
79
+ try:
80
+ collections = self.client.get_collections().collections
81
+ collection_names = [collection.name for collection in collections]
82
+
83
+ # Get vector dimension from the embedding model
84
+ vector_size = len(self.embeddings.embed_query("test"))
85
+
86
+ if COLLECTION_NAME not in collection_names:
87
+ # Create the collection with appropriate settings
88
+ self.client.create_collection(
89
+ collection_name=COLLECTION_NAME,
90
+ vectors_config=VectorParams(size=vector_size, distance=Distance.COSINE),
91
+ )
92
+ logger.info(f"Created new collection: {COLLECTION_NAME}")
93
+
94
+ return Qdrant(
95
+ client=self.client,
96
  collection_name=COLLECTION_NAME,
97
+ embeddings=self.embeddings
98
+ )
99
+ except Exception as e:
100
+ logger.error(f"Error initializing vector store: {e}")
101
+ # Create a simple in-memory fallback
102
+ logger.warning("Using in-memory vector store as fallback.")
103
+ return Qdrant.from_texts(
104
+ ["Hello, I am your AI assistant."],
105
+ self.embeddings,
106
+ location=":memory:",
107
+ collection_name=COLLECTION_NAME
108
  )
 
 
 
 
 
 
109
 
110
  def get_retriever(self):
111
  """Get the retriever for RAG."""
 
126
 
127
  def add_texts(self, texts, metadatas=None):
128
  """Add texts to the vector store."""
129
+ try:
130
+ return self.vectorstore.add_texts(texts=texts, metadatas=metadatas)
131
+ except Exception as e:
132
+ logger.error(f"Error adding texts to vector store: {e}")
133
+ return ["error-id-" + str(random.randint(10000, 99999))]
134
 
135
  def similarity_search(self, query, k=5):
136
  """Perform a similarity search."""
137
+ try:
138
+ return self.vectorstore.similarity_search(query, k=k)
139
+ except Exception as e:
140
+ logger.error(f"Error during similarity search: {e}")
141
+ return []
app/ui/streamlit_app.py CHANGED
@@ -4,6 +4,12 @@ import sys
4
  import tempfile
5
  from datetime import datetime
6
  from typing import List, Dict, Any
 
 
 
 
 
 
7
 
8
  # Add project root to path for imports
9
  sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
@@ -29,15 +35,50 @@ st.set_page_config(
29
  layout="wide"
30
  )
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # Initialize session state variables
33
  if "messages" not in st.session_state:
34
  st.session_state.messages = []
35
 
36
- if "agent" not in st.session_state:
37
- st.session_state.agent = AssistantAgent()
38
-
39
- if "document_processor" not in st.session_state:
40
- st.session_state.document_processor = DocumentProcessor(st.session_state.agent.memory_manager)
41
 
42
  # App title
43
  st.title("🤗 Personal AI Assistant (Hugging Face)")
@@ -64,7 +105,7 @@ with st.sidebar:
64
  f.write(uploaded_file.getvalue())
65
 
66
  # Ingest the document
67
- st.session_state.document_processor.ingest_file(tmp_path, {"original_name": uploaded_file.name})
68
 
69
  # Clean up the temporary file
70
  os.unlink(tmp_path)
@@ -87,7 +128,7 @@ with st.sidebar:
87
  }
88
 
89
  # Ingest the text
90
- st.session_state.document_processor.ingest_text(text_input, metadata)
91
 
92
  st.success("Text added to knowledge base successfully!")
93
  except Exception as e:
@@ -139,34 +180,57 @@ if prompt := st.chat_input("Ask a question..."):
139
  # Generate response
140
  with st.chat_message("assistant"):
141
  with st.spinner("Thinking..."):
142
- response = st.session_state.agent.query(prompt)
143
- answer = response["answer"]
144
- sources = response["sources"]
145
-
146
- # Display the response
147
- st.write(answer)
148
-
149
- # Display sources in an expander
150
- with st.expander("View Sources"):
151
- if sources:
152
- for i, source in enumerate(sources, 1):
153
- st.write(f"{i}. {source['file_name']}" + (f" (Page {source['page']})" if source.get('page') else ""))
154
- st.text(source['content'])
155
- else:
156
- st.write("No specific sources used.")
157
-
158
- # Save conversation
159
- save_conversation(prompt, answer, sources)
160
-
161
- # Add assistant response to chat history
162
- st.session_state.messages.append({
163
- "role": "assistant",
164
- "content": answer,
165
- "sources": sources
166
- })
167
-
168
- # Update the agent's memory
169
- st.session_state.agent.add_conversation_to_memory(prompt, answer)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  # Add a footer
172
  st.markdown("---")
 
4
  import tempfile
5
  from datetime import datetime
6
  from typing import List, Dict, Any
7
+ import time
8
+ import logging
9
+
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
 
14
  # Add project root to path for imports
15
  sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
 
35
  layout="wide"
36
  )
37
 
38
+ # Function to initialize the agent safely
39
+ @st.cache_resource
40
+ def get_agent():
41
+ logger.info("Initializing AssistantAgent (should only happen once)")
42
+ try:
43
+ return AssistantAgent()
44
+ except Exception as e:
45
+ logger.error(f"Error initializing agent: {e}")
46
+ st.error(f"Could not initialize AI assistant: {str(e)}")
47
+ # Return a dummy agent as fallback
48
+ class DummyAgent:
49
+ def query(self, question):
50
+ return {
51
+ "answer": "I'm having trouble starting up. Please try refreshing the page.",
52
+ "sources": []
53
+ }
54
+ def add_conversation_to_memory(self, *args, **kwargs):
55
+ pass
56
+ return DummyAgent()
57
+
58
+ # Function to initialize document processor safely
59
+ @st.cache_resource
60
+ def get_document_processor(agent):
61
+ logger.info("Initializing DocumentProcessor (should only happen once)")
62
+ try:
63
+ return DocumentProcessor(agent.memory_manager)
64
+ except Exception as e:
65
+ logger.error(f"Error initializing document processor: {e}")
66
+ st.error(f"Could not initialize document processor: {str(e)}")
67
+ # Return a dummy processor as fallback
68
+ class DummyProcessor:
69
+ def ingest_file(self, *args, **kwargs):
70
+ return ["dummy-id"]
71
+ def ingest_text(self, *args, **kwargs):
72
+ return ["dummy-id"]
73
+ return DummyProcessor()
74
+
75
  # Initialize session state variables
76
  if "messages" not in st.session_state:
77
  st.session_state.messages = []
78
 
79
+ # Initialize agent and document processor with caching to prevent multiple instances
80
+ agent = get_agent()
81
+ document_processor = get_document_processor(agent)
 
 
82
 
83
  # App title
84
  st.title("🤗 Personal AI Assistant (Hugging Face)")
 
105
  f.write(uploaded_file.getvalue())
106
 
107
  # Ingest the document
108
+ document_processor.ingest_file(tmp_path, {"original_name": uploaded_file.name})
109
 
110
  # Clean up the temporary file
111
  os.unlink(tmp_path)
 
128
  }
129
 
130
  # Ingest the text
131
+ document_processor.ingest_text(text_input, metadata)
132
 
133
  st.success("Text added to knowledge base successfully!")
134
  except Exception as e:
 
180
  # Generate response
181
  with st.chat_message("assistant"):
182
  with st.spinner("Thinking..."):
183
+ try:
184
+ # Add retry mechanism for vector store issues
185
+ max_retries = 3
186
+ for attempt in range(max_retries):
187
+ try:
188
+ response = agent.query(prompt)
189
+ break
190
+ except Exception as e:
191
+ if "already accessed by another instance" in str(e) and attempt < max_retries - 1:
192
+ logger.warning(f"Vector store access conflict, retrying ({attempt+1}/{max_retries})...")
193
+ time.sleep(1) # Wait before retrying
194
+ else:
195
+ raise
196
+
197
+ answer = response["answer"]
198
+ sources = response["sources"]
199
+
200
+ # Display the response
201
+ st.write(answer)
202
+
203
+ # Display sources in an expander
204
+ with st.expander("View Sources"):
205
+ if sources:
206
+ for i, source in enumerate(sources, 1):
207
+ st.write(f"{i}. {source['file_name']}" + (f" (Page {source['page']})" if source.get('page') else ""))
208
+ st.text(source['content'])
209
+ else:
210
+ st.write("No specific sources used.")
211
+
212
+ # Save conversation
213
+ save_conversation(prompt, answer, sources)
214
+
215
+ # Add assistant response to chat history
216
+ st.session_state.messages.append({
217
+ "role": "assistant",
218
+ "content": answer,
219
+ "sources": sources
220
+ })
221
+
222
+ # Update the agent's memory
223
+ agent.add_conversation_to_memory(prompt, answer)
224
+
225
+ except Exception as e:
226
+ error_msg = f"Error generating response: {str(e)}"
227
+ logger.error(error_msg)
228
+ st.error(error_msg)
229
+ st.session_state.messages.append({
230
+ "role": "assistant",
231
+ "content": "I'm sorry, I encountered an error while processing your request. Please try again or refresh the page.",
232
+ "sources": []
233
+ })
234
 
235
  # Add a footer
236
  st.markdown("---")