mgbam commited on
Commit
0f83924
·
verified ·
1 Parent(s): b26cbe4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -397
app.py CHANGED
@@ -1,462 +1,291 @@
 
 
 
 
1
  # ------------------------------
2
- # Imports & Dependencies
3
  # ------------------------------
4
- from langchain_openai import OpenAIEmbeddings
5
- from langchain_community.vectorstores import Chroma
6
- from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
 
 
 
 
 
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
 
 
8
  from langgraph.graph import END, StateGraph
9
  from langgraph.prebuilt import ToolNode
10
- from langgraph.graph.message import add_messages
11
  from typing_extensions import TypedDict, Annotated
12
- from typing import Sequence
13
- import chromadb
14
- import re
15
- import os
16
- import streamlit as st
17
- import requests
18
- from langchain.tools.retriever import create_retriever_tool
19
 
20
  # ------------------------------
21
- # Configuration
22
  # ------------------------------
23
- # Get DeepSeek API key from Hugging Face Space secrets
24
- DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY")
25
-
26
- if not DEEPSEEK_API_KEY:
27
- st.error("""
28
- **Missing API Configuration**
29
- Please configure your DeepSeek API key in Hugging Face Space secrets:
30
- 1. Go to your Space's Settings
31
- 2. Click on 'Repository secrets'
32
- 3. Add a secret named DEEPSEEK_API_KEY
33
- """)
34
- st.stop()
35
-
36
- # Create directory for Chroma persistence
37
- os.makedirs("chroma_db", exist_ok=True)
38
-
39
- # ------------------------------
40
- # ChromaDB Client Configuration
41
- # ------------------------------
42
- chroma_client = chromadb.PersistentClient(path="chroma_db")
43
-
44
- # ------------------------------
45
- # Dummy Data: Research & Development Texts
46
- # ------------------------------
47
- research_texts = [
48
- "Research Report: Results of a New AI Model Improving Image Recognition Accuracy to 98%",
49
- "Academic Paper Summary: Why Transformers Became the Mainstream Architecture in Natural Language Processing",
50
- "Latest Trends in Machine Learning Methods Using Quantum Computing"
51
- ]
52
-
53
- development_texts = [
54
- "Project A: UI Design Completed, API Integration in Progress",
55
- "Project B: Testing New Feature X, Bug Fixes Needed",
56
- "Product Y: In the Performance Optimization Stage Before Release"
57
- ]
58
-
59
- # ------------------------------
60
- # Text Splitting & Document Creation
61
- # ------------------------------
62
- splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=10)
63
- research_docs = splitter.create_documents(research_texts)
64
- development_docs = splitter.create_documents(development_texts)
65
 
66
  # ------------------------------
67
- # Creating Vector Stores with Embeddings
68
  # ------------------------------
69
- embeddings = OpenAIEmbeddings(
70
- model="text-embedding-3-large",
71
- # dimensions=1024 # Uncomment if needed
72
- )
73
-
74
- research_vectorstore = Chroma.from_documents(
75
- documents=research_docs,
76
- embedding=embeddings,
77
- client=chroma_client,
78
- collection_name="research_collection"
79
- )
80
-
81
- development_vectorstore = Chroma.from_documents(
82
- documents=development_docs,
83
- embedding=embeddings,
84
- client=chroma_client,
85
- collection_name="development_collection"
86
- )
87
-
88
- research_retriever = research_vectorstore.as_retriever()
89
- development_retriever = development_vectorstore.as_retriever()
 
90
 
91
- # ------------------------------
92
- # Creating Retriever Tools
93
- # ------------------------------
94
- research_tool = create_retriever_tool(
95
- research_retriever,
96
- "research_db_tool",
97
- "Search information from the research database."
98
- )
99
 
100
- development_tool = create_retriever_tool(
101
- development_retriever,
102
- "development_db_tool",
103
- "Search information from the development database."
104
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- tools = [research_tool, development_tool]
 
 
 
 
 
107
 
108
  # ------------------------------
109
- # Agent Function & Workflow Functions
110
  # ------------------------------
111
  class AgentState(TypedDict):
112
- messages: Annotated[Sequence[AIMessage | HumanMessage | ToolMessage], add_messages]
113
-
114
- def agent(state: AgentState):
115
- print("---CALL AGENT---")
116
- messages = state["messages"]
117
-
118
- if isinstance(messages[0], tuple):
119
- user_message = messages[0][1]
120
- else:
121
- user_message = messages[0].content
122
-
123
- prompt = f"""Given this user question: "{user_message}"
124
- If it's about research or academic topics, respond EXACTLY in this format:
125
- SEARCH_RESEARCH: <search terms>
126
 
127
- If it's about development status, respond EXACTLY in this format:
128
- SEARCH_DEV: <search terms>
129
-
130
- Otherwise, just answer directly.
131
- """
132
-
133
- headers = {
134
- "Accept": "application/json",
135
- "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
136
- "Content-Type": "application/json"
137
- }
138
-
139
- data = {
140
- "model": "deepseek-chat",
141
- "messages": [{"role": "user", "content": prompt}],
142
- "temperature": 0.7,
143
- "max_tokens": 1024
144
- }
145
-
146
  try:
147
- response = requests.post(
148
- "https://api.deepseek.com/v1/chat/completions",
149
- headers=headers,
150
- json=data,
151
- verify=False,
152
- timeout=30
153
- )
154
- response.raise_for_status()
155
 
156
- response_text = response.json()['choices'][0]['message']['content']
157
- print("Raw response:", response_text)
 
 
158
 
159
- if "SEARCH_RESEARCH:" in response_text:
160
- query = response_text.split("SEARCH_RESEARCH:")[1].strip()
161
- results = research_retriever.invoke(query)
162
- return {"messages": [AIMessage(content=f'Action: research_db_tool\n{{"query": "{query}"}}\n\nResults: {str(results)}')]}
163
-
164
- elif "SEARCH_DEV:" in response_text:
165
- query = response_text.split("SEARCH_DEV:")[1].strip()
166
- results = development_retriever.invoke(query)
167
- return {"messages": [AIMessage(content=f'Action: development_db_tool\n{{"query": "{query}"}}\n\nResults: {str(results)}')]}
168
-
169
- else:
170
- return {"messages": [AIMessage(content=response_text)]}
171
-
172
- except Exception as e:
173
- error_msg = f"API Error: {str(e)}"
174
- if "Insufficient Balance" in str(e):
175
- error_msg += "\n\nPlease check your DeepSeek API account balance."
176
- return {"messages": [AIMessage(content=error_msg)]}
177
-
178
- def simple_grade_documents(state: AgentState):
179
- messages = state["messages"]
180
- last_message = messages[-1]
181
- print("Evaluating message:", last_message.content)
182
-
183
- if "Results: [Document" in last_message.content:
184
- print("---DOCS FOUND, GO TO GENERATE---")
185
- return "generate"
186
- else:
187
- print("---NO DOCS FOUND, TRY REWRITE---")
188
- return "rewrite"
189
-
190
- def generate(state: AgentState):
191
- print("---GENERATE FINAL ANSWER---")
192
- messages = state["messages"]
193
- question = messages[0].content if isinstance(messages[0], tuple) else messages[0].content
194
- last_message = messages[-1]
195
-
196
- docs = ""
197
- if "Results: [" in last_message.content:
198
- results_start = last_message.content.find("Results: [")
199
- docs = last_message.content[results_start:]
200
- print("Documents found:", docs)
201
-
202
- headers = {
203
- "Accept": "application/json",
204
- "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
205
- "Content-Type": "application/json"
206
- }
207
-
208
- prompt = f"""Based on these research documents, summarize the latest advancements in AI:
209
- Question: {question}
210
- Documents: {docs}
211
- Focus on extracting and synthesizing the key findings from the research papers.
212
- """
213
-
214
- data = {
215
- "model": "deepseek-chat",
216
- "messages": [{
217
- "role": "user",
218
- "content": prompt
219
- }],
220
- "temperature": 0.7,
221
- "max_tokens": 1024
222
- }
223
-
224
- try:
225
- print("Sending generate request to API...")
226
  response = requests.post(
227
  "https://api.deepseek.com/v1/chat/completions",
228
  headers=headers,
229
- json=data,
230
- verify=False,
231
- timeout=30
 
 
 
 
 
 
 
 
 
232
  )
 
233
  response.raise_for_status()
 
234
 
235
- response_text = response.json()['choices'][0]['message']['content']
236
- print("Final Answer:", response_text)
237
- return {"messages": [AIMessage(content=response_text)]}
238
  except Exception as e:
239
- error_msg = f"Generation Error: {str(e)}"
240
- return {"messages": [AIMessage(content=error_msg)]}
241
-
242
- def rewrite(state: AgentState):
243
- print("---REWRITE QUESTION---")
244
- messages = state["messages"]
245
- original_question = messages[0].content if len(messages) > 0 else "N/A"
246
 
247
- headers = {
248
- "Accept": "application/json",
249
- "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
250
- "Content-Type": "application/json"
251
- }
252
-
253
- data = {
254
- "model": "deepseek-chat",
255
- "messages": [{
256
- "role": "user",
257
- "content": f"Rewrite this question to be more specific and clearer: {original_question}"
258
- }],
259
- "temperature": 0.7,
260
- "max_tokens": 1024
261
- }
262
-
263
  try:
264
- print("Sending rewrite request...")
 
 
 
 
 
 
265
  response = requests.post(
266
  "https://api.deepseek.com/v1/chat/completions",
267
  headers=headers,
268
- json=data,
269
- verify=False,
270
- timeout=30
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  )
272
- response.raise_for_status()
273
 
274
- response_text = response.json()['choices'][0]['message']['content']
275
- print("Rewritten question:", response_text)
276
- return {"messages": [AIMessage(content=response_text)]}
 
 
277
  except Exception as e:
278
- error_msg = f"Rewrite Error: {str(e)}"
279
- return {"messages": [AIMessage(content=error_msg)]}
280
-
281
- tools_pattern = re.compile(r"Action: .*")
282
-
283
- def custom_tools_condition(state: AgentState):
284
- messages = state["messages"]
285
- last_message = messages[-1]
286
- content = last_message.content
287
-
288
- print("Checking tools condition:", content)
289
- if tools_pattern.match(content):
290
- print("Moving to retrieve...")
291
- return "tools"
292
- print("Moving to END...")
293
- return END
294
-
295
- # ------------------------------
296
- # Workflow Configuration using LangGraph
297
- # ------------------------------
298
- workflow = StateGraph(AgentState)
299
-
300
- # Add nodes
301
- workflow.add_node("agent", agent)
302
- retrieve_node = ToolNode(tools)
303
- workflow.add_node("retrieve", retrieve_node)
304
- workflow.add_node("rewrite", rewrite)
305
- workflow.add_node("generate", generate)
306
-
307
- # Set entry point
308
- workflow.set_entry_point("agent")
309
-
310
- # Define transitions
311
- workflow.add_conditional_edges(
312
- "agent",
313
- custom_tools_condition,
314
- {
315
- "tools": "retrieve",
316
- END: END
317
- }
318
- )
319
-
320
- workflow.add_conditional_edges(
321
- "retrieve",
322
- simple_grade_documents,
323
- {
324
- "generate": "generate",
325
- "rewrite": "rewrite"
326
- }
327
- )
328
-
329
- workflow.add_edge("generate", END)
330
- workflow.add_edge("rewrite", "agent")
331
-
332
- # Compile the workflow
333
- app = workflow.compile()
334
 
335
  # ------------------------------
336
- # Processing Function
337
- # ------------------------------
338
- def process_question(user_question, app, config):
339
- """Process user question through the workflow"""
340
- events = []
341
- for event in app.stream({"messages": [("user", user_question)]}, config):
342
- events.append(event)
343
- return events
344
-
345
- # ------------------------------
346
- # Streamlit App UI (Dark Theme)
347
  # ------------------------------
348
  def main():
349
  st.set_page_config(
350
- page_title="AI Research & Development Assistant",
351
  layout="wide",
352
  initial_sidebar_state="expanded"
353
  )
354
-
 
355
  st.markdown("""
356
  <style>
357
  .stApp {
358
- background-color: #1a1a1a;
359
  color: #ffffff;
360
  }
361
-
362
- .stTextArea textarea {
363
- background-color: #2d2d2d !important;
364
  color: #ffffff !important;
365
  }
366
-
367
- .stButton > button {
368
- background-color: #4CAF50;
369
- color: white;
370
- transition: all 0.3s;
371
  }
372
-
373
- .stButton > button:hover {
374
- background-color: #45a049;
375
  transform: scale(1.02);
376
  }
377
-
378
- .data-box {
379
- background-color: #2d2d2d;
380
- border-left: 5px solid #2196F3;
381
- }
382
-
383
- .dev-box {
384
- border-left: 5px solid #4CAF50;
385
- }
386
-
387
- .st-expander {
388
- background-color: #2d2d2d;
389
- border: 1px solid #3d3d3d;
390
  }
391
  </style>
392
  """, unsafe_allow_html=True)
393
-
394
- with st.sidebar:
395
- st.header("📚 Available Data")
396
- st.subheader("Research Database")
397
- for text in research_texts:
398
- st.markdown(f'<div class="data-box research-box" style="padding: 15px; margin: 10px 0; border-radius: 5px;">{text}</div>', unsafe_allow_html=True)
399
-
400
- st.subheader("Development Database")
401
- for text in development_texts:
402
- st.markdown(f'<div class="data-box dev-box" style="padding: 15px; margin: 10px 0; border-radius: 5px;">{text}</div>', unsafe_allow_html=True)
403
-
404
- st.title("🤖 AI Research & Development Assistant")
405
- st.markdown("---")
406
-
407
- query = st.text_area("Enter your question:", height=100, placeholder="e.g., What is the latest advancement in AI research?")
408
-
409
  col1, col2 = st.columns([1, 2])
 
410
  with col1:
411
- if st.button("🔍 Get Answer", use_container_width=True):
412
- if query:
413
- try:
414
- with st.spinner('Processing your question...'):
415
- events = process_question(query, app, {"configurable": {"thread_id": "1"}})
416
-
417
- for event in events:
418
- if 'agent' in event:
419
- with st.expander("🔄 Processing Step", expanded=True):
420
- content = event['agent']['messages'][0].content
421
- if "Error" in content:
422
- st.error(content)
423
- elif "Results:" in content:
424
- st.markdown("### 📑 Retrieved Documents:")
425
- docs_start = content.find("Results:")
426
- docs = content[docs_start:]
427
- st.info(docs)
428
- elif 'generate' in event:
429
- content = event['generate']['messages'][0].content
430
- if "Error" in content:
431
- st.error(content)
432
- else:
433
- st.markdown("### ✨ Final Answer:")
434
- st.success(content)
435
- except Exception as e:
436
- st.error(f"""
437
- **Processing Error**
438
- {str(e)}
439
- Please check:
440
- - API key configuration
441
- - Account balance
442
- - Network connection
443
- """)
444
- else:
445
- st.warning("⚠️ Please enter a question first!")
446
-
447
  with col2:
448
- st.markdown("""
449
- ### 🎯 How to Use
450
- 1. Enter your question in the text box
451
- 2. Click the search button
452
- 3. Review processing steps
453
- 4. See final answer
454
-
455
- ### 💡 Example Questions
456
- - What's new in AI image recognition?
457
- - How is Project B progressing?
458
- - Recent machine learning trends?
459
- """)
 
 
460
 
461
  if __name__ == "__main__":
 
 
 
 
 
 
462
  main()
 
1
+ """
2
+ AI Research Assistant - Professional Edition
3
+ """
4
+
5
  # ------------------------------
6
+ # Core Imports & Configuration
7
  # ------------------------------
8
+ import os
9
+ import re
10
+ import time
11
+ import chromadb
12
+ import requests
13
+ import streamlit as st
14
+ from typing import Sequence, Tuple
15
+ from langchain_core.messages import HumanMessage, AIMessage
16
  from langchain.text_splitter import RecursiveCharacterTextSplitter
17
+ from langchain_community.vectorstores import Chroma
18
+ from langchain.tools.retriever import create_retriever_tool
19
  from langgraph.graph import END, StateGraph
20
  from langgraph.prebuilt import ToolNode
 
21
  from typing_extensions import TypedDict, Annotated
22
+ from chromadb.config import Settings
 
 
 
 
 
 
23
 
24
  # ------------------------------
25
+ # Configuration & Constants
26
  # ------------------------------
27
+ class Config:
28
+ API_KEY = os.environ.get("DEEPSEEK_API_KEY")
29
+ CHROMA_PATH = "chroma_db"
30
+ TEXT_SPLITTER_CONFIG = {
31
+ "chunk_size": 512,
32
+ "chunk_overlap": 128,
33
+ "separators": ["\n\n", "\n", ". ", "! ", "? "]
34
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  # ------------------------------
37
+ # Core System Components
38
  # ------------------------------
39
+ class ResearchAssistant:
40
+ def __init__(self):
41
+ self.embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
42
+ self.vector_stores = self._init_vector_stores()
43
+ self.tools = self._create_tools()
44
+ self.workflow = self._build_workflow()
45
+
46
+ def _init_vector_stores(self) -> Tuple[Chroma, Chroma]:
47
+ """Initialize and return research & development vector stores"""
48
+ splitter = RecursiveCharacterTextSplitter(**Config.TEXT_SPLITTER_CONFIG)
49
+
50
+ research_docs = splitter.create_documents([
51
+ "Research Report: New AI Model Achieves 98% Image Recognition Accuracy",
52
+ "Transformers: The New NLP Architecture Standard",
53
+ "Quantum Machine Learning: Emerging Trends and Applications"
54
+ ])
55
+
56
+ development_docs = splitter.create_documents([
57
+ "Project A: UI Design Finalized, API Integration Phase",
58
+ "Project B: Feature Testing and Bug Fixes",
59
+ "Product Y: Performance Optimization Pre-Release"
60
+ ])
61
 
62
+ client = chromadb.PersistentClient(path=Config.CHROMA_PATH)
63
+
64
+ return (
65
+ Chroma.from_documents(research_docs, self.embeddings,
66
+ client=client, collection_name="research"),
67
+ Chroma.from_documents(development_docs, self.embeddings,
68
+ client=client, collection_name="development")
69
+ )
70
 
71
+ def _create_tools(self):
72
+ """Create retrieval tools with optimized search parameters"""
73
+ research_retriever = self.vector_stores[0].as_retriever(
74
+ search_kwargs={"k": 3, "score_threshold": 0.7}
75
+ )
76
+ development_retriever = self.vector_stores[1].as_retriever(
77
+ search_kwargs={"k": 3, "score_threshold": 0.7}
78
+ )
79
+
80
+ return [
81
+ create_retriever_tool(
82
+ research_retriever,
83
+ "research_db",
84
+ "Access technical research papers and reports"
85
+ ),
86
+ create_retriever_tool(
87
+ development_retriever,
88
+ "development_db",
89
+ "Retrieve project development status updates"
90
+ )
91
+ ]
92
+
93
+ def _build_workflow(self):
94
+ """Construct and return the processing workflow"""
95
+ workflow = StateGraph(AgentState)
96
+
97
+ workflow.add_node("analyze", self.analyze_query)
98
+ workflow.add_node("retrieve", ToolNode(self.tools))
99
+ workflow.add_node("synthesize", self.synthesize_response)
100
+
101
+ workflow.set_entry_point("analyze")
102
+
103
+ workflow.add_conditional_edges(
104
+ "analyze",
105
+ self._needs_retrieval,
106
+ {"retrieve": "retrieve", "direct": "synthesize"}
107
+ )
108
+
109
+ workflow.add_edge("retrieve", "synthesize")
110
+ workflow.add_edge("synthesize", END)
111
+
112
+ return workflow.compile()
113
 
114
+ def _needs_retrieval(self, state: AgentState) -> str:
115
+ """Determine if document retrieval is needed"""
116
+ query = state["messages"][-1].content.lower()
117
+ return "retrieve" if any(kw in query for kw in {
118
+ "research", "study", "project", "develop", "trend"
119
+ }) else "direct"
120
 
121
  # ------------------------------
122
+ # Processing Functions
123
  # ------------------------------
124
  class AgentState(TypedDict):
125
+ messages: Annotated[Sequence[AIMessage], add_messages]
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ def analyze_query(state: AgentState):
128
+ """Analyze user query and determine next steps"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  try:
130
+ user_input = state["messages"][-1].content
 
 
 
 
 
 
 
131
 
132
+ headers = {
133
+ "Authorization": f"Bearer {Config.API_KEY}",
134
+ "Content-Type": "application/json"
135
+ }
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  response = requests.post(
138
  "https://api.deepseek.com/v1/chat/completions",
139
  headers=headers,
140
+ json={
141
+ "model": "deepseek-chat",
142
+ "messages": [{
143
+ "role": "user",
144
+ "content": f"""Analyze this query and format as:
145
+ CATEGORY: [RESEARCH|DEVELOPMENT|GENERAL]
146
+ KEY_TERMS: comma-separated list
147
+ {user_input}"""
148
+ }],
149
+ "temperature": 0.3
150
+ },
151
+ timeout=15
152
  )
153
+
154
  response.raise_for_status()
155
+ analysis = response.json()["choices"][0]["message"]["content"]
156
 
157
+ return {"messages": [AIMessage(content=analysis)]}
158
+
 
159
  except Exception as e:
160
+ return {"messages": [AIMessage(
161
+ content=f"Analysis Error: {str(e)}. Please rephrase your question."
162
+ )]}
 
 
 
 
163
 
164
+ def synthesize_response(state: AgentState):
165
+ """Generate final response with citations"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  try:
167
+ context = "\n".join([msg.content for msg in state["messages"]])
168
+
169
+ headers = {
170
+ "Authorization": f"Bearer {Config.API_KEY}",
171
+ "Content-Type": "application/json"
172
+ }
173
+
174
  response = requests.post(
175
  "https://api.deepseek.com/v1/chat/completions",
176
  headers=headers,
177
+ json={
178
+ "model": "deepseek-chat",
179
+ "messages": [{
180
+ "role": "user",
181
+ "content": f"""Synthesize this information into a professional report:
182
+ {context}
183
+
184
+ Include:
185
+ 1. Key findings
186
+ 2. Supporting evidence
187
+ 3. Technical details
188
+ 4. Potential applications"""
189
+ }],
190
+ "temperature": 0.5
191
+ },
192
+ timeout=20
193
  )
 
194
 
195
+ response.raise_for_status()
196
+ return {"messages": [AIMessage(
197
+ content=response.json()["choices"][0]["message"]["content"]
198
+ )]}
199
+
200
  except Exception as e:
201
+ return {"messages": [AIMessage(
202
+ content=f"Synthesis Error: {str(e)}. Please try again later."
203
+ )]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
  # ------------------------------
206
+ # Professional UI Interface
 
 
 
 
 
 
 
 
 
 
207
  # ------------------------------
208
  def main():
209
  st.set_page_config(
210
+ page_title="Research Assistant Pro",
211
  layout="wide",
212
  initial_sidebar_state="expanded"
213
  )
214
+
215
+ # Dark theme implementation
216
  st.markdown("""
217
  <style>
218
  .stApp {
219
+ background-color: #0f1114;
220
  color: #ffffff;
221
  }
222
+ .stTextInput input, .stTextArea textarea {
223
+ background-color: #1e1e24 !important;
 
224
  color: #ffffff !important;
225
  }
226
+ .stButton>button {
227
+ background: #2563eb;
228
+ transition: all 0.2s;
 
 
229
  }
230
+ .stButton>button:hover {
231
+ background: #1d4ed8;
 
232
  transform: scale(1.02);
233
  }
234
+ .result-card {
235
+ background: #1a1a1f;
236
+ border-radius: 8px;
237
+ padding: 1.5rem;
238
+ margin: 1rem 0;
 
 
 
 
 
 
 
 
239
  }
240
  </style>
241
  """, unsafe_allow_html=True)
242
+
243
+ st.title("🔍 Research Assistant Pro")
244
+ st.write("Advanced AI-Powered Research Analysis")
245
+
 
 
 
 
 
 
 
 
 
 
 
 
246
  col1, col2 = st.columns([1, 2])
247
+
248
  with col1:
249
+ with st.form("query_form"):
250
+ query = st.text_area("Research Query:", height=150,
251
+ placeholder="Enter your research question...")
252
+ submitted = st.form_submit_button("Analyze")
253
+
254
+ if submitted and query:
255
+ with st.spinner("Processing..."):
256
+ assistant = ResearchAssistant()
257
+ result = assistant.workflow.invoke({"messages": [
258
+ HumanMessage(content=query)
259
+ ]})
260
+
261
+ with st.expander("Analysis Details", expanded=True):
262
+ st.markdown(f"""
263
+ <div class="result-card">
264
+ {result['messages'][-1].content}
265
+ </div>
266
+ """, unsafe_allow_html=True)
267
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  with col2:
269
+ st.subheader("Knowledge Base")
270
+ with st.expander("Research Documents"):
271
+ st.info("""
272
+ - Advanced Image Recognition Systems
273
+ - Transformer Architecture Analysis
274
+ - Quantum ML Research
275
+ """)
276
+
277
+ with st.expander("Development Updates"):
278
+ st.info("""
279
+ - Project A: API Integration Phase
280
+ - Project B: Feature Testing
281
+ - Product Y: Optimization Stage
282
+ """)
283
 
284
  if __name__ == "__main__":
285
+ if not Config.API_KEY:
286
+ st.error("""
287
+ 🔑 Configuration Required:
288
+ Set DEEPSEEK_API_KEY in environment variables
289
+ """)
290
+ st.stop()
291
  main()