mgbam commited on
Commit
b97aaed
Β·
verified Β·
1 Parent(s): 2e9c284

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -364
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # ------------------------------
2
- # Imports & Dependencies
3
  # ------------------------------
4
  from langchain_openai import OpenAIEmbeddings
5
  from langchain_community.vectorstores import Chroma
@@ -21,7 +21,7 @@ from langchain.tools.retriever import create_retriever_tool
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%",
@@ -40,7 +40,7 @@ development_texts = [
40
  ]
41
 
42
  # ------------------------------
43
- # Configuration Class
44
  # ------------------------------
45
  class AppConfig:
46
  def __init__(self):
@@ -52,49 +52,43 @@ class AppConfig:
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):
58
  if not self.DEEPSEEK_API_KEY:
59
  st.error("""
60
- **Critical Configuration Missing**
61
- πŸ”‘ DeepSeek API key not found in environment variables.
62
- Please configure through Hugging Face Space secrets:
63
- 1. Go to Space Settings β†’ Repository secrets
64
- 2. Add secret: Name=DEEPSEEK_API_KEY, Value=your_api_key
65
  3. Rebuild Space
66
  """)
67
  st.stop()
68
 
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,
95
- separators=["\n\n", "\n", "。", " "]
96
  )
97
- docs = text_splitter.create_documents(documents)
98
  return Chroma.from_documents(
99
  documents=docs,
100
  embedding=self.embeddings,
@@ -103,383 +97,208 @@ class ChromaManager:
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:
118
- content_hash = hashlib.md5(doc.page_content.encode()).hexdigest()
119
- if content_hash not in seen:
120
- unique_docs.append(doc)
121
- seen.add(content_hash)
122
- return unique_docs
123
 
124
  @staticmethod
125
- def extract_key_points(docs: List[Any]) -> str:
126
- key_points = []
127
  categories = {
128
- "quantum": ["quantum", "qpu", "qubit"],
129
- "vision": ["image", "recognition", "vision"],
130
- "nlp": ["transformer", "language", "llm"]
131
  }
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"]):
138
- key_points.append("- Computer vision models achieving state-of-the-art accuracy")
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):
149
- self.session_stats = {
150
- "processing_times": [],
151
- "doc_counts": [],
152
- "error_count": 0
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"
159
- }
160
 
161
- for attempt in range(config.MAX_RETRIES):
162
- try:
163
- response = requests.post(
164
- endpoint,
165
- headers=headers,
166
- json=payload,
167
- timeout=30,
168
- verify=False
169
- )
170
- response.raise_for_status()
171
- return response.json()
172
- except requests.exceptions.HTTPError as e:
173
- if e.response.status_code == 429:
174
- delay = config.RETRY_DELAY ** (attempt + 1)
175
- time.sleep(delay)
176
- continue
177
- raise
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"
353
- secondary_color = "#28B463"
354
- background_color = "#1A1A1A"
355
- text_color = "#EAECEE"
356
-
357
- @classmethod
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
- }}
367
- .stButton > button {{
368
- background-color: {cls.primary_color};
369
- color: white;
370
- border: none;
371
- padding: 12px 28px;
372
- border-radius: 6px;
373
- transition: all 0.3s ease;
374
- font-weight: 500;
375
- }}
376
- .stButton > button:hover {{
377
- background-color: {cls.secondary_color};
378
- transform: translateY(-1px);
379
- box-shadow: 0 4px 12px rgba(0,0,0,0.2);
380
- }}
381
- .data-box {{
382
- background-color: #2D2D2D;
383
- border-left: 4px solid {cls.primary_color};
384
- padding: 18px;
385
- margin: 14px 0;
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
-
395
- st.set_page_config(
396
- page_title="AI Research Assistant Pro",
397
- layout="wide",
398
- initial_sidebar_state="expanded",
399
- menu_items={
400
- 'Get Help': 'https://example.com/docs',
401
- 'Report a bug': 'https://example.com/issues',
402
- 'About': "v2.1 | Enhanced Research Assistant"
403
- }
404
- )
405
 
406
  with st.sidebar:
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])
427
- with col1:
428
- if st.button("πŸš€ Analyze Documents", use_container_width=True):
429
- if not query:
430
- st.warning("⚠️ Please enter a research question")
431
- return
 
432
 
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()
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ------------------------------
2
+ # Imports
3
  # ------------------------------
4
  from langchain_openai import OpenAIEmbeddings
5
  from langchain_community.vectorstores import Chroma
 
21
  from datetime import datetime
22
 
23
  # ------------------------------
24
+ # Data
25
  # ------------------------------
26
  research_texts = [
27
  "Research Report: Results of a New AI Model Improving Image Recognition Accuracy to 98%",
 
40
  ]
41
 
42
  # ------------------------------
43
+ # Configuration
44
  # ------------------------------
45
  class AppConfig:
46
  def __init__(self):
 
52
  self.DOCUMENT_OVERLAP = 50
53
  self.SEARCH_K = 5
54
  self.SEARCH_TYPE = "mmr"
55
+
56
+ def validate(self):
 
57
  if not self.DEEPSEEK_API_KEY:
58
  st.error("""
59
+ **Configuration Error**
60
+ πŸ”‘ Missing DeepSeek API key.
61
+ Configure through Hugging Face Space secrets:
62
+ 1. Space Settings β†’ Repository secrets
63
+ 2. Add secret: DEEPSEEK_API_KEY=your_key
64
  3. Rebuild Space
65
  """)
66
  st.stop()
67
 
 
 
 
 
 
68
  class ChromaManager:
69
+ def __init__(self, config: AppConfig):
70
  os.makedirs(config.CHROMA_PATH, exist_ok=True)
71
  self.client = chromadb.PersistentClient(path=config.CHROMA_PATH)
72
  self.embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
73
 
74
+ self.research_collection = self._create_collection(
75
+ research_texts,
76
  "research_collection",
77
+ {"category": "research"}
78
  )
79
+ self.dev_collection = self._create_collection(
80
+ development_texts,
81
  "development_collection",
82
+ {"category": "development"}
83
  )
84
 
85
+ def _create_collection(self, documents: List[str], name: str, metadata: dict) -> Chroma:
86
+ splitter = RecursiveCharacterTextSplitter(
87
+ chunk_size=300,
88
+ chunk_overlap=50,
89
+ separators=["\n\n", "\n", "。"]
90
  )
91
+ docs = splitter.create_documents(documents)
92
  return Chroma.from_documents(
93
  documents=docs,
94
  embedding=self.embeddings,
 
97
  collection_metadata=metadata
98
  )
99
 
 
 
 
100
  # ------------------------------
101
  # Document Processing
102
  # ------------------------------
103
  class DocumentProcessor:
104
  @staticmethod
105
+ def deduplicate(docs: List[Any]) -> List[Any]:
106
  seen = set()
107
+ return [doc for doc in docs
108
+ if not (hashlib.md5(doc.page_content.encode()).hexdigest() in seen
109
+ or seen.add(hashlib.md5(doc.page_content.encode()).hexdigest()))]
 
 
 
 
110
 
111
  @staticmethod
112
+ def extract_keypoints(docs: List[Any]) -> str:
 
113
  categories = {
114
+ "quantum": ["quantum", "qubit"],
115
+ "vision": ["image", "recognition"],
116
+ "nlp": ["transformer", "language"]
117
  }
118
+ return "\n".join(sorted({
119
+ "- " + {
120
+ "quantum": "Quantum computing breakthroughs",
121
+ "vision": "Computer vision advancements",
122
+ "nlp": "NLP architecture innovations"
123
+ }[cat]
124
+ for doc in docs
125
+ for cat, kw in categories.items()
126
+ if any(k in doc.page_content.lower() for k in kw)
127
+ }))
 
128
 
129
  # ------------------------------
130
+ # Workflow
131
  # ------------------------------
132
+ class AgentWorkflow:
133
+ def __init__(self, chroma: ChromaManager):
134
+ self.chroma = chroma
135
+ self.workflow = StateGraph(AgentState)
 
 
 
 
 
 
 
 
 
136
 
137
+ # Define nodes
138
+ self.workflow.add_node("agent", self.agent)
139
+ self.workflow.add_node("retrieve", ToolNode([
140
+ create_retriever_tool(
141
+ chroma.research_collection.as_retriever(),
142
+ "research_tool",
143
+ "Search research documents"
144
+ ),
145
+ create_retriever_tool(
146
+ chroma.dev_collection.as_retriever(),
147
+ "dev_tool",
148
+ "Search development updates"
149
+ )
150
+ ]))
151
+ self.workflow.add_node("generate", self.generate)
152
+ self.workflow.add_node("rewrite", self.rewrite)
 
 
153
 
154
+ # Define edges
155
+ self.workflow.set_entry_point("agent")
156
+ self.workflow.add_conditional_edges(
157
+ "agent",
158
+ self._tools_condition,
159
+ {"retrieve": "retrieve", "end": END}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  )
161
+ self.workflow.add_conditional_edges(
162
+ "retrieve",
163
+ self._grade_documents,
164
+ {"generate": "generate", "rewrite": "rewrite"}
165
+ )
166
+ self.workflow.add_edge("generate", END)
167
+ self.workflow.add_edge("rewrite", "agent")
 
 
 
 
 
168
 
169
+ self.app = self.workflow.compile()
 
 
 
 
 
170
 
171
+ class AgentState(TypedDict):
172
+ messages: Annotated[Sequence[AIMessage | HumanMessage | ToolMessage], add_messages]
 
 
173
 
174
+ def agent(self, state: AgentState):
175
+ try:
176
+ messages = state["messages"]
177
+ query = messages[-1].content if isinstance(messages[-1], HumanMessage) else messages[-1]['content']
178
+
179
+ response = requests.post(
180
+ "https://api.deepseek.com/v1/chat/completions",
181
+ headers={"Authorization": f"Bearer {config.DEEPSEEK_API_KEY}"},
182
+ json={
183
+ "model": "deepseek-chat",
184
+ "messages": [{
185
+ "role": "user",
186
+ "content": f"""Analyze this query: "{query}"
187
+ Respond EXACTLY as:
188
+ - SEARCH_RESEARCH: <terms> (for research topics)
189
+ - SEARCH_DEV: <terms> (for development updates)
190
+ - DIRECT: <answer> (otherwise)"""
191
+ }]
192
+ }
193
+ ).json()
194
+
195
+ content = response['choices'][0]['message']['content']
196
+ if "SEARCH_RESEARCH:" in content:
197
+ terms = content.split("SEARCH_RESEARCH:")[1].strip()
198
+ results = self.chroma.research_collection.similarity_search(terms)
199
+ return {"messages": [AIMessage(content=f"Research Results: {str(results)}")]}
200
+ elif "SEARCH_DEV:" in content:
201
+ terms = content.split("SEARCH_DEV:")[1].strip()
202
+ results = self.chroma.dev_collection.similarity_search(terms)
203
+ return {"messages": [AIMessage(content=f"Development Results: {str(results)}")]}
204
+ return {"messages": [AIMessage(content=content)]}
205
+
206
+ except Exception as e:
207
+ return {"messages": [AIMessage(content=f"Error: {str(e)}")]}
208
 
209
+ def generate(self, state: AgentState):
210
+ docs = eval(state["messages"][-1].content.split("Results: ")[1])
211
+ processed = "\n".join([d.page_content[:200] for d in DocumentProcessor.deduplicate(docs)])
212
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  response = requests.post(
214
  "https://api.deepseek.com/v1/chat/completions",
215
+ headers={"Authorization": f"Bearer {config.DEEPSEEK_API_KEY}"},
 
 
 
216
  json={
217
  "model": "deepseek-chat",
218
+ "messages": [{
219
+ "role": "user",
220
+ "content": f"Summarize these findings:\n{processed}"
221
+ }]
222
+ }
223
+ ).json()
224
+
225
+ return {"messages": [AIMessage(content=response['choices'][0]['message']['content'])]}
 
 
226
 
227
+ def rewrite(self, state: AgentState):
228
+ original = state["messages"][0].content
 
 
 
229
  response = requests.post(
230
  "https://api.deepseek.com/v1/chat/completions",
231
+ headers={"Authorization": f"Bearer {config.DEEPSEEK_API_KEY}"},
 
 
 
232
  json={
233
  "model": "deepseek-chat",
234
  "messages": [{
235
+ "role": "user",
236
+ "content": f"Rephrase this query: {original}"
237
+ }]
238
+ }
239
+ ).json()
240
+ return {"messages": [AIMessage(content=response['choices'][0]['message']['content'])]}
 
 
 
 
 
 
241
 
242
+ def _tools_condition(self, state: AgentState):
243
+ return "retrieve" if "Results:" in state["messages"][-1].content else "end"
244
 
245
+ def _grade_documents(self, state: AgentState):
246
+ return "generate" if len(eval(state["messages"][-1].content.split("Results: ")[1])) > 0 else "rewrite"
 
247
 
248
  # ------------------------------
249
+ # Streamlit App
250
  # ------------------------------
251
+ def apply_theme():
252
+ st.markdown("""
253
+ <style>
254
+ .stApp { background: #1a1a1a; color: white; }
255
+ .stTextArea textarea { background: #2d2d2d !important; color: white !important; }
256
+ .stButton>button { background: #2E86C1; transition: 0.3s; }
257
+ .stButton>button:hover { background: #1B4F72; transform: scale(1.02); }
258
+ .data-box { background: #2d2d2d; border-left: 4px solid #2E86C1; padding: 15px; margin: 10px 0; }
259
+ </style>
260
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  def main():
263
+ apply_theme()
 
 
 
 
 
 
 
 
 
 
 
264
 
265
  with st.sidebar:
266
+ st.header("πŸ“š Databases")
267
+ with st.expander("Research", expanded=True):
268
  for text in research_texts:
269
+ st.markdown(f'<div class="data-box">{text}</div>', unsafe_allow_html=True)
270
+ with st.expander("Development"):
 
271
  for text in development_texts:
272
+ st.markdown(f'<div class="data-box">{text}</div>', unsafe_allow_html=True)
273
+
274
+ st.title("πŸ” AI Research Assistant")
275
+ query = st.text_area("Enter your query:", height=100)
 
 
 
 
 
 
 
276
 
277
+ if st.button("Analyze"):
278
+ with st.spinner("Processing..."):
279
+ workflow = AgentWorkflow(chroma_manager)
280
+ results = workflow.app.invoke({"messages": [HumanMessage(content=query)]})
281
+
282
+ with st.expander("Processing Details", expanded=True):
283
+ st.write("### Raw Results", results)
284
 
285
+ st.success("### Final Answer")
286
+ st.markdown(results['messages'][-1].content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
+ # ------------------------------
289
+ # Initialization
290
+ # ------------------------------
291
  if __name__ == "__main__":
292
+ st.set_page_config(
293
+ page_title="AI Research Assistant",
294
+ layout="wide",
295
+ initial_sidebar_state="expanded"
296
+ )
297
+
298
+ try:
299
+ config = AppConfig()
300
+ config.validate()
301
+ chroma_manager = ChromaManager(config)
302
+ main()
303
+ except Exception as e:
304
+ st.error(f"Initialization failed: {str(e)}")