mgbam commited on
Commit
d94f105
Β·
verified Β·
1 Parent(s): be1f54f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -99
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # ------------------------------
2
- # Imports & Dependencies (Enhanced)
3
  # ------------------------------
4
  from langchain_openai import OpenAIEmbeddings
5
  from langchain_community.vectorstores import Chroma
@@ -21,7 +21,26 @@ from langchain.tools.retriever import create_retriever_tool
21
  from datetime import datetime
22
 
23
  # ------------------------------
24
- # Enhanced Configuration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # ------------------------------
26
  class AppConfig:
27
  def __init__(self):
@@ -29,11 +48,10 @@ class AppConfig:
29
  self.CHROMA_PATH = "chroma_db"
30
  self.MAX_RETRIES = 3
31
  self.RETRY_DELAY = 1.5
32
- self.DOCUMENT_CHUNK_SIZE = 300 # Increased from 100
33
- self.DOCUMENT_OVERLAP = 50 # Added overlap for context preservation
34
- self.SEARCH_K = 5 # Number of documents to retrieve
35
- self.SEARCH_TYPE = "mmr" # Maximal Marginal Relevance
36
-
37
  self.validate_config()
38
 
39
  def validate_config(self):
@@ -51,19 +69,26 @@ class AppConfig:
51
  config = AppConfig()
52
 
53
  # ------------------------------
54
- # Enhanced ChromaDB Setup
55
  # ------------------------------
56
  class ChromaManager:
57
- def __init__(self):
58
  os.makedirs(config.CHROMA_PATH, exist_ok=True)
59
  self.client = chromadb.PersistentClient(path=config.CHROMA_PATH)
60
- self.embeddings = OpenAIEmbeddings(
61
- model="text-embedding-3-large",
62
- # dimensions=1024 # Optional for large-scale deployments
63
- )
64
 
65
- def create_collection(self, documents: List[str], collection_name: str) -> Chroma:
66
- """Enhanced document processing with optimized chunking"""
 
 
 
 
 
 
 
 
 
 
67
  text_splitter = RecursiveCharacterTextSplitter(
68
  chunk_size=config.DOCUMENT_CHUNK_SIZE,
69
  chunk_overlap=config.DOCUMENT_OVERLAP,
@@ -74,34 +99,19 @@ class ChromaManager:
74
  documents=docs,
75
  embedding=self.embeddings,
76
  client=self.client,
77
- collection_name=collection_name
 
78
  )
79
 
80
- # Initialize Chroma with improved parameters
81
- chroma_manager = ChromaManager()
82
- research_collection = chroma_manager.create_collection(research_texts, "research_collection")
83
- dev_collection = chroma_manager.create_collection(development_texts, "development_collection")
84
-
85
- # ------------------------------
86
- # Enhanced Retriever Configuration
87
- # ------------------------------
88
- research_retriever = research_collection.as_retriever(
89
- search_type=config.SEARCH_TYPE,
90
- search_kwargs={"k": config.SEARCH_K, "fetch_k": config.SEARCH_K * 2}
91
- )
92
-
93
- development_retriever = dev_collection.as_retriever(
94
- search_type=config.SEARCH_TYPE,
95
- search_kwargs={"k": config.SEARCH_K, "fetch_k": config.SEARCH_K * 2}
96
- )
97
 
98
  # ------------------------------
99
- # Enhanced Document Processing
100
  # ------------------------------
101
  class DocumentProcessor:
102
  @staticmethod
103
  def deduplicate_documents(docs: List[Any]) -> List[Any]:
104
- """Advanced deduplication using content hashing"""
105
  seen = set()
106
  unique_docs = []
107
  for doc in docs:
@@ -113,7 +123,6 @@ class DocumentProcessor:
113
 
114
  @staticmethod
115
  def extract_key_points(docs: List[Any]) -> str:
116
- """Semantic analysis of retrieved documents"""
117
  key_points = []
118
  categories = {
119
  "quantum": ["quantum", "qpu", "qubit"],
@@ -123,7 +132,6 @@ class DocumentProcessor:
123
 
124
  for doc in docs:
125
  content = doc.page_content.lower()
126
- # Categorization logic
127
  if any(kw in content for kw in categories["quantum"]):
128
  key_points.append("- Quantum computing integration showing promising results")
129
  if any(kw in content for kw in categories["vision"]):
@@ -131,10 +139,10 @@ class DocumentProcessor:
131
  if any(kw in content for kw in categories["nlp"]):
132
  key_points.append("- NLP architectures evolving with memory-augmented transformers")
133
 
134
- return "\n".join(list(set(key_points))) # Remove duplicates
135
 
136
  # ------------------------------
137
- # Enhanced Agent Workflow (Additions)
138
  # ------------------------------
139
  class EnhancedAgent:
140
  def __init__(self):
@@ -145,7 +153,6 @@ class EnhancedAgent:
145
  }
146
 
147
  def api_request_with_retry(self, endpoint: str, payload: Dict) -> Dict:
148
- """Robust API handling with exponential backoff"""
149
  headers = {
150
  "Authorization": f"Bearer {config.DEEPSEEK_API_KEY}",
151
  "Content-Type": "application/json"
@@ -171,7 +178,175 @@ class EnhancedAgent:
171
  raise Exception(f"API request failed after {config.MAX_RETRIES} attempts")
172
 
173
  # ------------------------------
174
- # Enhanced Streamlit UI (Dark Professional Theme)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  # ------------------------------
176
  class UITheme:
177
  primary_color = "#2E86C1"
@@ -183,12 +358,9 @@ class UITheme:
183
  def apply(cls):
184
  st.markdown(f"""
185
  <style>
186
- .stApp {{
187
- background-color: {cls.background_color};
188
- color: {cls.text_color};
189
- }}
190
- .stTextArea textarea {{
191
- background-color: #2D2D2D !important;
192
  color: {cls.text_color} !important;
193
  border: 1px solid {cls.primary_color};
194
  }}
@@ -214,22 +386,9 @@ class UITheme:
214
  border-radius: 8px;
215
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
216
  }}
217
- .st-expander {{
218
- background-color: #2D2D2D;
219
- border: 1px solid #3D3D3D;
220
- border-radius: 6px;
221
- margin: 12px 0;
222
- }}
223
- .stAlert {{
224
- background-color: #423a2d !important;
225
- border: 1px solid #E67E22 !important;
226
- }}
227
  </style>
228
  """, unsafe_allow_html=True)
229
 
230
- # ------------------------------
231
- # Enhanced Main Application
232
- # ------------------------------
233
  def main():
234
  UITheme.apply()
235
 
@@ -248,23 +407,20 @@ def main():
248
  st.header("πŸ“‚ Knowledge Bases")
249
  with st.expander("Research Database", expanded=True):
250
  for text in research_texts:
251
- st.markdown(f'<div class="data-box research-box">{text}</div>',
252
- unsafe_allow_html=True)
253
 
254
  with st.expander("Development Database"):
255
  for text in development_texts:
256
- st.markdown(f'<div class="data-box dev-box">{text}</div>',
257
- unsafe_allow_html=True)
258
 
259
  st.title("πŸ”¬ AI Research Assistant Pro")
260
  st.markdown("---")
261
 
262
- # Enhanced query input with examples
263
  query = st.text_area(
264
  "Research Query Input",
265
  height=120,
266
- placeholder="Enter your research question...\nExample: What are recent breakthroughs in quantum machine learning?",
267
- help="Be specific about domains (e.g., computer vision, NLP) for better results"
268
  )
269
 
270
  col1, col2 = st.columns([1, 2])
@@ -277,72 +433,53 @@ def main():
277
  with st.status("Processing Workflow...", expanded=True) as status:
278
  try:
279
  start_time = time.time()
280
-
281
- # Document Retrieval Phase
282
- status.update(label="πŸ” Retrieving Relevant Documents", state="running")
283
  events = process_question(query, app, {"configurable": {"thread_id": "1"}})
284
 
285
- # Processing Phase
286
- status.update(label="πŸ“Š Analyzing Content", state="running")
287
  processed_data = []
288
-
289
  for event in events:
290
  if 'agent' in event:
291
  content = event['agent']['messages'][0].content
292
  if "Results:" in content:
293
- docs_str = content.split("Results: ")[1]
294
- docs = eval(docs_str)
295
  unique_docs = DocumentProcessor.deduplicate_documents(docs)
296
  key_points = DocumentProcessor.extract_key_points(unique_docs)
297
  processed_data.append(key_points)
298
 
299
  with st.expander("πŸ“„ Retrieved Documents", expanded=False):
300
  st.info(f"Found {len(unique_docs)} unique documents")
301
- st.write(docs_str)
302
 
303
  elif 'generate' in event:
304
  final_answer = event['generate']['messages'][0].content
305
  status.update(label="βœ… Analysis Complete", state="complete")
306
-
307
  st.markdown("## πŸ“ Research Summary")
308
  st.markdown(final_answer)
309
 
310
- # Performance metrics
311
- proc_time = time.time() - start_time
312
- st.caption(f"⏱️ Processed in {proc_time:.2f}s | {len(processed_data)} document clusters")
313
 
314
  except Exception as e:
315
  status.update(label="❌ Processing Failed", state="error")
316
- st.error(f"""
317
- **Critical Error**
318
- {str(e)}
319
- Recommended Actions:
320
- - Verify API key configuration
321
- - Check service status
322
- - Simplify query complexity
323
- """)
324
- # Log error with timestamp
325
- error_log = f"{datetime.now()} | {str(e)}\n"
326
  with open("error_log.txt", "a") as f:
327
- f.write(error_log)
328
 
329
  with col2:
330
  st.markdown("""
331
  ## πŸ“˜ Usage Guide
332
  **1. Query Formulation**
333
- - Be domain-specific (e.g., "quantum NLP")
334
- - Include timeframes (e.g., "2023-2024 advances")
335
 
336
  **2. Results Interpretation**
337
- - Expand document sections for sources
338
- - Key points highlight technical breakthroughs
339
- - Summary shows commercial implications
340
 
341
  **3. Advanced Features**
342
- - `CTRL+Enter` for quick reruns
343
  - Click documents for raw context
344
- - Export results via screenshot
345
  """)
346
 
347
  if __name__ == "__main__":
348
- main()
 
1
  # ------------------------------
2
+ # Imports & Dependencies
3
  # ------------------------------
4
  from langchain_openai import OpenAIEmbeddings
5
  from langchain_community.vectorstores import Chroma
 
21
  from datetime import datetime
22
 
23
  # ------------------------------
24
+ # Data Definitions
25
+ # ------------------------------
26
+ research_texts = [
27
+ "Research Report: Results of a New AI Model Improving Image Recognition Accuracy to 98%",
28
+ "Academic Paper Summary: Why Transformers Became the Mainstream Architecture in Natural Language Processing",
29
+ "Latest Trends in Machine Learning Methods Using Quantum Computing",
30
+ "Advancements in Neuromorphic Computing for Energy-Efficient AI Systems",
31
+ "Cross-Modal Learning: Integrating Visual and Textual Representations for Multimodal AI"
32
+ ]
33
+
34
+ development_texts = [
35
+ "Project A: UI Design Completed, API Integration in Progress",
36
+ "Project B: Testing New Feature X, Bug Fixes Needed",
37
+ "Product Y: In the Performance Optimization Stage Before Release",
38
+ "Framework Z: Version 3.2 Released with Enhanced Distributed Training Support",
39
+ "DevOps Pipeline: Automated CI/CD Implementation for ML Model Deployment"
40
+ ]
41
+
42
+ # ------------------------------
43
+ # Configuration Class
44
  # ------------------------------
45
  class AppConfig:
46
  def __init__(self):
 
48
  self.CHROMA_PATH = "chroma_db"
49
  self.MAX_RETRIES = 3
50
  self.RETRY_DELAY = 1.5
51
+ self.DOCUMENT_CHUNK_SIZE = 300
52
+ self.DOCUMENT_OVERLAP = 50
53
+ self.SEARCH_K = 5
54
+ self.SEARCH_TYPE = "mmr"
 
55
  self.validate_config()
56
 
57
  def validate_config(self):
 
69
  config = AppConfig()
70
 
71
  # ------------------------------
72
+ # ChromaDB Manager
73
  # ------------------------------
74
  class ChromaManager:
75
+ def __init__(self, research_data: List[str], development_data: List[str]):
76
  os.makedirs(config.CHROMA_PATH, exist_ok=True)
77
  self.client = chromadb.PersistentClient(path=config.CHROMA_PATH)
78
+ self.embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
 
 
 
79
 
80
+ self.research_collection = self.create_collection(
81
+ research_data,
82
+ "research_collection",
83
+ {"category": "research", "version": "1.2"}
84
+ )
85
+ self.dev_collection = self.create_collection(
86
+ development_data,
87
+ "development_collection",
88
+ {"category": "development", "version": "1.1"}
89
+ )
90
+
91
+ def create_collection(self, documents: List[str], name: str, metadata: dict) -> Chroma:
92
  text_splitter = RecursiveCharacterTextSplitter(
93
  chunk_size=config.DOCUMENT_CHUNK_SIZE,
94
  chunk_overlap=config.DOCUMENT_OVERLAP,
 
99
  documents=docs,
100
  embedding=self.embeddings,
101
  client=self.client,
102
+ collection_name=name,
103
+ collection_metadata=metadata
104
  )
105
 
106
+ # Initialize Chroma with data
107
+ chroma_manager = ChromaManager(research_texts, development_texts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  # ------------------------------
110
+ # Document Processing
111
  # ------------------------------
112
  class DocumentProcessor:
113
  @staticmethod
114
  def deduplicate_documents(docs: List[Any]) -> List[Any]:
 
115
  seen = set()
116
  unique_docs = []
117
  for doc in docs:
 
123
 
124
  @staticmethod
125
  def extract_key_points(docs: List[Any]) -> str:
 
126
  key_points = []
127
  categories = {
128
  "quantum": ["quantum", "qpu", "qubit"],
 
132
 
133
  for doc in docs:
134
  content = doc.page_content.lower()
 
135
  if any(kw in content for kw in categories["quantum"]):
136
  key_points.append("- Quantum computing integration showing promising results")
137
  if any(kw in content for kw in categories["vision"]):
 
139
  if any(kw in content for kw in categories["nlp"]):
140
  key_points.append("- NLP architectures evolving with memory-augmented transformers")
141
 
142
+ return "\n".join(list(set(key_points)))
143
 
144
  # ------------------------------
145
+ # Enhanced Agent Components
146
  # ------------------------------
147
  class EnhancedAgent:
148
  def __init__(self):
 
153
  }
154
 
155
  def api_request_with_retry(self, endpoint: str, payload: Dict) -> Dict:
 
156
  headers = {
157
  "Authorization": f"Bearer {config.DEEPSEEK_API_KEY}",
158
  "Content-Type": "application/json"
 
178
  raise Exception(f"API request failed after {config.MAX_RETRIES} attempts")
179
 
180
  # ------------------------------
181
+ # Workflow Configuration
182
+ # ------------------------------
183
+ class AgentState(TypedDict):
184
+ messages: Annotated[Sequence[AIMessage | HumanMessage | ToolMessage], add_messages]
185
+
186
+ def agent(state: AgentState):
187
+ print("---CALL AGENT---")
188
+ messages = state["messages"]
189
+ user_message = messages[0].content if not isinstance(messages[0], tuple) else messages[0][1]
190
+
191
+ prompt = f"""Given this user question: "{user_message}"
192
+ If about research/academic topics, respond EXACTLY:
193
+ SEARCH_RESEARCH: <search terms>
194
+ If about development status, respond EXACTLY:
195
+ SEARCH_DEV: <search terms>
196
+ Otherwise, answer directly."""
197
+
198
+ headers = {
199
+ "Accept": "application/json",
200
+ "Authorization": f"Bearer {config.DEEPSEEK_API_KEY}",
201
+ "Content-Type": "application/json"
202
+ }
203
+
204
+ data = {
205
+ "model": "deepseek-chat",
206
+ "messages": [{"role": "user", "content": prompt}],
207
+ "temperature": 0.7,
208
+ "max_tokens": 1024
209
+ }
210
+
211
+ try:
212
+ response = requests.post(
213
+ "https://api.deepseek.com/v1/chat/completions",
214
+ headers=headers,
215
+ json=data,
216
+ verify=False,
217
+ timeout=30
218
+ )
219
+ response.raise_for_status()
220
+ response_text = response.json()['choices'][0]['message']['content']
221
+
222
+ if "SEARCH_RESEARCH:" in response_text:
223
+ query = response_text.split("SEARCH_RESEARCH:")[1].strip()
224
+ results = chroma_manager.research_collection.as_retriever().invoke(query)
225
+ return {"messages": [AIMessage(content=f'Action: research_db_tool\n{{"query": "{query}"}}\n\nResults: {str(results)}')]
226
+
227
+ elif "SEARCH_DEV:" in response_text:
228
+ query = response_text.split("SEARCH_DEV:")[1].strip()
229
+ results = chroma_manager.dev_collection.as_retriever().invoke(query)
230
+ return {"messages": [AIMessage(content=f'Action: development_db_tool\n{{"query": "{query}"}}\n\nResults: {str(results)}')]
231
+
232
+ return {"messages": [AIMessage(content=response_text)]}
233
+ except Exception as e:
234
+ error_msg = f"API Error: {str(e)}"
235
+ if "Insufficient Balance" in str(e):
236
+ error_msg += "\n\nPlease check your DeepSeek API account balance."
237
+ return {"messages": [AIMessage(content=error_msg)]}
238
+
239
+ def simple_grade_documents(state: AgentState):
240
+ messages = state["messages"]
241
+ last_message = messages[-1]
242
+ return "generate" if "Results: [Document" in last_message.content else "rewrite"
243
+
244
+ def generate(state: AgentState):
245
+ messages = state["messages"]
246
+ question = messages[0].content
247
+ last_message = messages[-1]
248
+
249
+ docs_content = []
250
+ if "Results: [" in last_message.content:
251
+ docs_str = last_message.content.split("Results: ")[1]
252
+ docs_content = eval(docs_str)
253
+
254
+ processed_info = DocumentProcessor.extract_key_points(
255
+ DocumentProcessor.deduplicate_documents(docs_content)
256
+ )
257
+
258
+ prompt = f"""Generate structured research summary:
259
+ Key Information:
260
+ {processed_info}
261
+ Include:
262
+ 1. Section headings
263
+ 2. Bullet points
264
+ 3. Significance
265
+ 4. Applications"""
266
+
267
+ try:
268
+ response = requests.post(
269
+ "https://api.deepseek.com/v1/chat/completions",
270
+ headers={
271
+ "Authorization": f"Bearer {config.DEEPSEEK_API_KEY}",
272
+ "Content-Type": "application/json"
273
+ },
274
+ json={
275
+ "model": "deepseek-chat",
276
+ "messages": [{"role": "user", "content": prompt}],
277
+ "temperature": 0.7,
278
+ "max_tokens": 1024
279
+ },
280
+ timeout=30
281
+ )
282
+ response.raise_for_status()
283
+ return {"messages": [AIMessage(content=response.json()['choices'][0]['message']['content'])}
284
+ except Exception as e:
285
+ return {"messages": [AIMessage(content=f"Generation Error: {str(e)}")]}
286
+
287
+ def rewrite(state: AgentState):
288
+ messages = state["messages"]
289
+ original_question = messages[0].content
290
+
291
+ try:
292
+ response = requests.post(
293
+ "https://api.deepseek.com/v1/chat/completions",
294
+ headers={
295
+ "Authorization": f"Bearer {config.DEEPSEEK_API_KEY}",
296
+ "Content-Type": "application/json"
297
+ },
298
+ json={
299
+ "model": "deepseek-chat",
300
+ "messages": [{
301
+ "role": "user",
302
+ "content": f"Rewrite for clarity: {original_question}"
303
+ }],
304
+ "temperature": 0.7,
305
+ "max_tokens": 1024
306
+ },
307
+ timeout=30
308
+ )
309
+ response.raise_for_status()
310
+ return {"messages": [AIMessage(content=response.json()['choices'][0]['message']['content'])}
311
+ except Exception as e:
312
+ return {"messages": [AIMessage(content=f"Rewrite Error: {str(e)}")]}
313
+
314
+ tools_pattern = re.compile(r"Action: .*")
315
+
316
+ def custom_tools_condition(state: AgentState):
317
+ content = state["messages"][-1].content
318
+ return "tools" if tools_pattern.match(content) else END
319
+
320
+ # ------------------------------
321
+ # Workflow Graph Setup
322
+ # ------------------------------
323
+ workflow = StateGraph(AgentState)
324
+ workflow.add_node("agent", agent)
325
+ workflow.add_node("retrieve", ToolNode([
326
+ create_retriever_tool(
327
+ chroma_manager.research_collection.as_retriever(),
328
+ "research_db_tool",
329
+ "Search research database"
330
+ ),
331
+ create_retriever_tool(
332
+ chroma_manager.dev_collection.as_retriever(),
333
+ "development_db_tool",
334
+ "Search development database"
335
+ )
336
+ ]))
337
+ workflow.add_node("rewrite", rewrite)
338
+ workflow.add_node("generate", generate)
339
+
340
+ workflow.set_entry_point("agent")
341
+ workflow.add_conditional_edges("agent", custom_tools_condition, {"tools": "retrieve", END: END})
342
+ workflow.add_conditional_edges("retrieve", simple_grade_documents, {"generate": "generate", "rewrite": "rewrite"})
343
+ workflow.add_edge("generate", END)
344
+ workflow.add_edge("rewrite", "agent")
345
+
346
+ app = workflow.compile()
347
+
348
+ # ------------------------------
349
+ # Streamlit UI
350
  # ------------------------------
351
  class UITheme:
352
  primary_color = "#2E86C1"
 
358
  def apply(cls):
359
  st.markdown(f"""
360
  <style>
361
+ .stApp {{ background-color: {cls.background_color}; color: {cls.text_color}; }}
362
+ .stTextArea textarea {{
363
+ background-color: #2D2D2D !important;
 
 
 
364
  color: {cls.text_color} !important;
365
  border: 1px solid {cls.primary_color};
366
  }}
 
386
  border-radius: 8px;
387
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
388
  }}
 
 
 
 
 
 
 
 
 
 
389
  </style>
390
  """, unsafe_allow_html=True)
391
 
 
 
 
392
  def main():
393
  UITheme.apply()
394
 
 
407
  st.header("πŸ“‚ Knowledge Bases")
408
  with st.expander("Research Database", expanded=True):
409
  for text in research_texts:
410
+ st.markdown(f'<div class="data-box research-box">{text}</div>', unsafe_allow_html=True)
 
411
 
412
  with st.expander("Development Database"):
413
  for text in development_texts:
414
+ st.markdown(f'<div class="data-box dev-box">{text}</div>', unsafe_allow_html=True)
 
415
 
416
  st.title("πŸ”¬ AI Research Assistant Pro")
417
  st.markdown("---")
418
 
 
419
  query = st.text_area(
420
  "Research Query Input",
421
  height=120,
422
+ placeholder="Enter your research question...",
423
+ help="Be specific about domains for better results"
424
  )
425
 
426
  col1, col2 = st.columns([1, 2])
 
433
  with st.status("Processing Workflow...", expanded=True) as status:
434
  try:
435
  start_time = time.time()
 
 
 
436
  events = process_question(query, app, {"configurable": {"thread_id": "1"}})
437
 
 
 
438
  processed_data = []
 
439
  for event in events:
440
  if 'agent' in event:
441
  content = event['agent']['messages'][0].content
442
  if "Results:" in content:
443
+ docs = eval(content.split("Results: ")[1])
 
444
  unique_docs = DocumentProcessor.deduplicate_documents(docs)
445
  key_points = DocumentProcessor.extract_key_points(unique_docs)
446
  processed_data.append(key_points)
447
 
448
  with st.expander("πŸ“„ Retrieved Documents", expanded=False):
449
  st.info(f"Found {len(unique_docs)} unique documents")
450
+ st.write(docs)
451
 
452
  elif 'generate' in event:
453
  final_answer = event['generate']['messages'][0].content
454
  status.update(label="βœ… Analysis Complete", state="complete")
 
455
  st.markdown("## πŸ“ Research Summary")
456
  st.markdown(final_answer)
457
 
458
+ st.caption(f"⏱️ Processed in {time.time()-start_time:.2f}s | {len(processed_data)} clusters")
 
 
459
 
460
  except Exception as e:
461
  status.update(label="❌ Processing Failed", state="error")
462
+ st.error(f"**Error:** {str(e)}\n\nCheck API key and network connection")
 
 
 
 
 
 
 
 
 
463
  with open("error_log.txt", "a") as f:
464
+ f.write(f"{datetime.now()} | {str(e)}\n")
465
 
466
  with col2:
467
  st.markdown("""
468
  ## πŸ“˜ Usage Guide
469
  **1. Query Formulation**
470
+ - Specify domains (e.g., "quantum NLP")
471
+ - Include timeframes for recent advances
472
 
473
  **2. Results Interpretation**
474
+ - Expand sections for source documents
475
+ - Key points show technical breakthroughs
476
+ - Summary includes commercial implications
477
 
478
  **3. Advanced Features**
479
+ - Use keyboard shortcuts for efficiency
480
  - Click documents for raw context
481
+ - Export via screenshot/PDF
482
  """)
483
 
484
  if __name__ == "__main__":
485
+ main()