rdune71 commited on
Commit
bf25842
·
1 Parent(s): 488f34a

Implemented live web search (Tavily), academic research access (Semantic Scholar), and evaluation dashboard with tabs

Browse files
AI-Life-Coach-Streamlit2 ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit ac83e06dd776b60459bb531d961b4f327d1e6a41
app.py CHANGED
@@ -14,6 +14,7 @@ from core.session import session_manager
14
  from core.memory import check_redis_health
15
  from core.coordinator import coordinator
16
  from core.errors import translate_error
 
17
  import logging
18
 
19
  # Set up logging
@@ -43,312 +44,469 @@ if "ngrok_url_temp" not in st.session_state:
43
  if "hf_expert_requested" not in st.session_state:
44
  st.session_state.hf_expert_requested = False
45
 
46
- # Sidebar layout redesign
47
- with st.sidebar:
48
- st.title("🧠 AI Life Coach")
49
- st.markdown("Your personal AI-powered life development assistant")
50
-
51
- # PRIMARY ACTIONS
52
- st.subheader("💬 Primary Actions")
53
- model_options = {
54
- "Mistral 7B (Local)": "mistral:latest",
55
- "Llama 2 7B (Local)": "llama2:latest",
56
- "OpenChat 3.5 (Local)": "openchat:latest"
57
- }
58
- selected_model_name = st.selectbox(
59
- "Select Model",
60
- options=list(model_options.keys()),
61
- index=0,
62
- key="sidebar_model_select"
63
- )
64
- st.session_state.selected_model = model_options[selected_model_name]
65
-
66
- st.divider()
67
-
68
- # CONFIGURATION
69
- st.subheader("⚙️ Configuration")
70
- ngrok_url_input = st.text_input(
71
- "Ollama Server URL",
72
- value=st.session_state.ngrok_url_temp,
73
- help="Enter your ngrok URL",
74
- key="sidebar_ngrok_url"
75
- )
76
-
77
- if ngrok_url_input != st.session_state.ngrok_url_temp:
78
- st.session_state.ngrok_url_temp = ngrok_url_input
79
- st.success("✅ URL updated!")
80
-
81
- if st.button("📡 Test Connection"):
82
- try:
83
- import requests
84
- headers = {
85
- "ngrok-skip-browser-warning": "true",
86
- "User-Agent": "AI-Life-Coach-Test"
87
- }
88
- with st.spinner("Testing connection..."):
89
- response = requests.get(
90
- f"{ngrok_url_input}/api/tags",
91
- headers=headers,
92
- timeout=15
93
- )
94
- if response.status_code == 200:
95
- st.success("✅ Connection successful!")
96
- else:
97
- st.error(f"❌ Failed: {response.status_code}")
98
- except Exception as e:
99
- st.error(f"❌ Error: {str(e)[:50]}...")
100
-
101
- if st.button("🗑️ Clear History"):
102
- st.session_state.messages = []
103
- st.success("History cleared!")
104
-
105
- st.divider()
106
-
107
- # ADVANCED FEATURES
108
- with st.expander("🔍 Advanced Features", expanded=False):
109
- st.subheader("📊 System Monitor")
110
- try:
111
- from services.ollama_monitor import check_ollama_status
112
- ollama_status = check_ollama_status()
113
- if ollama_status.get("running"):
114
- st.success("🦙 Ollama: Running")
115
- else:
116
- st.warning("🦙 Ollama: Not running")
117
- except:
118
- st.info("🦙 Ollama: Unknown")
119
 
120
- try:
121
- from services.hf_endpoint_monitor import hf_monitor
122
- hf_status = hf_monitor.check_endpoint_status()
123
- if hf_status['available']:
124
- st.success("🤗 HF: Available")
125
- else:
126
- st.warning("🤗 HF: Not available")
127
- except:
128
- st.info("🤗 HF: Unknown")
 
 
 
 
 
129
 
130
- if check_redis_health():
131
- st.success("💾 Redis: Connected")
132
- else:
133
- st.error("💾 Redis: Disconnected")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  st.divider()
136
 
137
- st.subheader("🤖 HF Expert Analysis")
138
- col1, col2 = st.columns([3, 1])
139
- with col1:
140
- st.markdown("""
141
- **HF Expert Features:**
142
- - Analyzes entire conversation history
143
- - Performs web research when needed
144
- - Provides deep insights and recommendations
145
- - Acts as expert consultant in your conversation
146
- """)
147
- with col2:
148
- if st.button("🧠 Activate HF Expert",
149
- key="activate_hf_expert_sidebar",
150
- help="Send conversation to HF endpoint for deep analysis",
151
- use_container_width=True,
152
- disabled=st.session_state.is_processing):
153
- st.session_state.hf_expert_requested = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- # Main interface
156
- st.title("🧠 AI Life Coach")
157
- st.markdown("Ask me anything about personal development, goal setting, or life advice!")
158
 
159
- # Display messages
160
- for message in st.session_state.messages:
161
- with st.chat_message(message["role"]):
162
- # Format HF expert messages differently
163
- if message.get("source") == "hf_expert":
164
- st.markdown("### 🤖 HF Expert Analysis")
165
- st.markdown(message["content"])
166
- else:
167
- st.markdown(message["content"])
168
- if "timestamp" in message:
169
- st.caption(f"🕒 {message['timestamp']}")
170
 
171
- # Manual HF Analysis Section
172
- if st.session_state.messages and len(st.session_state.messages) > 0:
173
- st.divider()
174
-
175
- # HF Expert Section
176
- with st.expander("�� HF Expert Analysis", expanded=False):
177
- st.subheader("Deep Conversation Analysis")
178
 
179
- col1, col2 = st.columns([3, 1])
180
- with col1:
181
- st.markdown("""
182
- **HF Expert Features:**
183
- - Analyzes entire conversation history
184
- - Performs web research when needed
185
- - Provides deep insights and recommendations
186
- - Acts as expert consultant in your conversation
187
- """)
188
 
189
- # Show conversation preview
190
- st.markdown("**Conversation Preview for HF Expert:**")
191
- st.markdown("---")
192
- for i, msg in enumerate(st.session_state.messages[-5:]): # Last 5 messages
193
- role = "👤 You" if msg["role"] == "user" else "🤖 Assistant"
194
- st.markdown(f"**{role}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
195
- st.markdown("---")
196
-
197
- with col2:
198
- if st.button("🧠 Activate HF Expert",
199
- key="activate_hf_expert",
200
- help="Send conversation to HF endpoint for deep analysis",
201
- use_container_width=True,
202
- disabled=st.session_state.is_processing):
203
- st.session_state.hf_expert_requested = True
204
-
205
- # Show HF expert analysis when requested
206
- if st.session_state.get("hf_expert_requested", False):
207
- with st.spinner("🧠 HF Expert analyzing conversation..."):
208
- try:
209
- # Get conversation history
210
- user_session = session_manager.get_session("default_user")
211
- conversation_history = user_session.get("conversation", [])
212
-
213
- # Show what HF expert sees
214
- with st.expander("📋 HF Expert Input", expanded=False):
215
- st.markdown("**Conversation History Sent to HF Expert:**")
216
- for i, msg in enumerate(conversation_history[-10:]): # Last 10 messages
217
- st.markdown(f"**{msg['role'].capitalize()}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
218
-
219
- # Request HF analysis
220
- hf_analysis = coordinator.manual_hf_analysis(
221
- "default_user",
222
- conversation_history
223
- )
224
-
225
- if hf_analysis:
226
- # Display HF expert response with clear indication
227
- with st.chat_message("assistant"):
228
- st.markdown("### 🤖 HF Expert Analysis")
229
- st.markdown(hf_analysis)
230
 
231
- # Add research/web search decisions
232
- research_needs = coordinator.determine_web_search_needs(conversation_history)
233
- if research_needs["needs_search"]:
234
- st.info(f"🔍 **Research Needed:** {research_needs['reasoning']}")
235
- if st.button("🔎 Perform Web Research", key="web_research_button"):
236
- # Perform web search
237
- with st.spinner("🔎 Searching for current information..."):
238
- # Add web search logic here
239
- st.success("✅ Web research completed!")
240
 
241
- # Add to message history with HF expert tag
242
- st.session_state.messages.append({
243
- "role": "assistant",
244
- "content": hf_analysis,
245
- "timestamp": datetime.now().strftime("%H:%M:%S"),
246
- "source": "hf_expert",
247
- "research_needs": research_needs
248
- })
249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  st.session_state.hf_expert_requested = False
251
-
252
- except Exception as e:
253
- user_msg = translate_error(e)
254
- st.error(f"❌ HF Expert analysis failed: {user_msg}")
255
- st.session_state.hf_expert_requested = False
256
 
257
- # Chat input - FIXED VERSION
258
- user_input = st.chat_input("Type your message here...", disabled=st.session_state.is_processing)
259
 
260
- # Process message when received
261
- if user_input and not st.session_state.is_processing:
262
- st.session_state.is_processing = True
263
-
264
- # Display user message
265
- with st.chat_message("user"):
266
- st.markdown(user_input)
267
-
268
- st.session_state.messages.append({
269
- "role": "user",
270
- "content": user_input,
271
- "timestamp": datetime.now().strftime("%H:%M:%S")
272
- })
273
-
274
- # Process AI response
275
- with st.chat_message("assistant"):
276
- response_placeholder = st.empty()
277
- status_placeholder = st.empty()
278
 
279
- try:
280
- # Get conversation history
281
- user_session = session_manager.get_session("default_user")
282
- conversation = user_session.get("conversation", [])
283
- conversation_history = conversation[-5:] # Last 5 messages
284
- conversation_history.append({"role": "user", "content": user_input})
285
-
286
- # Try Ollama with proper error handling
287
- status_placeholder.info(PROCESSING_STAGES["ollama"])
288
- ai_response = None
 
 
 
 
289
 
290
  try:
291
- ai_response = send_to_ollama(
292
- user_input,
293
- conversation_history,
294
- st.session_state.ngrok_url_temp,
295
- st.session_state.selected_model
296
- )
 
 
 
297
 
298
- if ai_response:
299
- response_placeholder.markdown(ai_response)
300
- status_placeholder.success("✅ Response received!")
301
- else:
302
- status_placeholder.warning("⚠️ Empty response from Ollama")
303
-
304
- except Exception as ollama_error:
305
- user_msg = translate_error(ollama_error)
306
- status_placeholder.error(f"⚠️ {user_msg}")
307
-
308
- # Fallback to HF if available
309
- if config.hf_token and not ai_response:
310
- status_placeholder.info(PROCESSING_STAGES["hf_init"])
311
  try:
312
- ai_response = send_to_hf(user_input, conversation_history)
 
 
 
 
 
 
313
  if ai_response:
314
  response_placeholder.markdown(ai_response)
315
- status_placeholder.success("✅ HF response received!")
316
  else:
317
- status_placeholder.error(" No response from HF")
318
- except Exception as hf_error:
319
- user_msg = translate_error(hf_error)
 
320
  status_placeholder.error(f"⚠️ {user_msg}")
321
-
322
- # Save response if successful
323
- if ai_response:
324
- # Update conversation history
325
- conversation.append({"role": "user", "content": user_input})
326
- conversation.append({"role": "assistant", "content": ai_response})
327
- user_session["conversation"] = conversation
328
- session_manager.update_session("default_user", user_session)
329
 
330
- # Add to message history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  st.session_state.messages.append({
332
  "role": "assistant",
333
- "content": ai_response,
334
  "timestamp": datetime.now().strftime("%H:%M:%S")
335
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  else:
337
- st.session_state.messages.append({
338
- "role": "assistant",
339
- "content": "Sorry, I couldn't process your request. Please try again.",
340
- "timestamp": datetime.now().strftime("%H:%M:%S")
341
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- except Exception as e:
344
- user_msg = translate_error(e)
345
- response_placeholder.error(f"⚠️ {user_msg}")
346
- st.session_state.messages.append({
347
- "role": "assistant",
348
- "content": f"⚠️ {user_msg}",
349
- "timestamp": datetime.now().strftime("%H:%M:%S")
350
- })
351
- finally:
352
- st.session_state.is_processing = False
353
- time.sleep(0.5) # Brief pause
354
- st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  from core.memory import check_redis_health
15
  from core.coordinator import coordinator
16
  from core.errors import translate_error
17
+ from services.research.papers import find_papers
18
  import logging
19
 
20
  # Set up logging
 
44
  if "hf_expert_requested" not in st.session_state:
45
  st.session_state.hf_expert_requested = False
46
 
47
+ # Add evaluation dashboard tab
48
+ tab1, tab2, tab3 = st.tabs(["💬 Chat", "🔬 Evaluate AI", "📊 Reports"])
49
+
50
+ with tab1:
51
+ # Sidebar layout redesign
52
+ with st.sidebar:
53
+ st.title("🧠 AI Life Coach")
54
+ st.markdown("Your personal AI-powered life development assistant")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ # PRIMARY ACTIONS
57
+ st.subheader("💬 Primary Actions")
58
+ model_options = {
59
+ "Mistral 7B (Local)": "mistral:latest",
60
+ "Llama 2 7B (Local)": "llama2:latest",
61
+ "OpenChat 3.5 (Local)": "openchat:latest"
62
+ }
63
+ selected_model_name = st.selectbox(
64
+ "Select Model",
65
+ options=list(model_options.keys()),
66
+ index=0,
67
+ key="sidebar_model_select"
68
+ )
69
+ st.session_state.selected_model = model_options[selected_model_name]
70
 
71
+ st.divider()
72
+
73
+ # CONFIGURATION
74
+ st.subheader("⚙️ Configuration")
75
+ ngrok_url_input = st.text_input(
76
+ "Ollama Server URL",
77
+ value=st.session_state.ngrok_url_temp,
78
+ help="Enter your ngrok URL",
79
+ key="sidebar_ngrok_url"
80
+ )
81
+
82
+ if ngrok_url_input != st.session_state.ngrok_url_temp:
83
+ st.session_state.ngrok_url_temp = ngrok_url_input
84
+ st.success("✅ URL updated!")
85
+
86
+ if st.button("📡 Test Connection"):
87
+ try:
88
+ import requests
89
+ headers = {
90
+ "ngrok-skip-browser-warning": "true",
91
+ "User-Agent": "AI-Life-Coach-Test"
92
+ }
93
+ with st.spinner("Testing connection..."):
94
+ response = requests.get(
95
+ f"{ngrok_url_input}/api/tags",
96
+ headers=headers,
97
+ timeout=15
98
+ )
99
+ if response.status_code == 200:
100
+ st.success("✅ Connection successful!")
101
+ else:
102
+ st.error(f"❌ Failed: {response.status_code}")
103
+ except Exception as e:
104
+ st.error(f"❌ Error: {str(e)[:50]}...")
105
+
106
+ if st.button("🗑️ Clear History"):
107
+ st.session_state.messages = []
108
+ st.success("History cleared!")
109
 
110
  st.divider()
111
 
112
+ # ADVANCED FEATURES
113
+ with st.expander("🔍 Advanced Features", expanded=False):
114
+ st.subheader("📊 System Monitor")
115
+ try:
116
+ from services.ollama_monitor import check_ollama_status
117
+ ollama_status = check_ollama_status()
118
+ if ollama_status.get("running"):
119
+ st.success("🦙 Ollama: Running")
120
+ else:
121
+ st.warning("🦙 Ollama: Not running")
122
+ except:
123
+ st.info("🦙 Ollama: Unknown")
124
+
125
+ try:
126
+ from services.hf_endpoint_monitor import hf_monitor
127
+ hf_status = hf_monitor.check_endpoint_status()
128
+ if hf_status['available']:
129
+ st.success("🤗 HF: Available")
130
+ else:
131
+ st.warning("🤗 HF: Not available")
132
+ except:
133
+ st.info("🤗 HF: Unknown")
134
+
135
+ if check_redis_health():
136
+ st.success("💾 Redis: Connected")
137
+ else:
138
+ st.error("💾 Redis: Disconnected")
139
+
140
+ st.divider()
141
+
142
+ st.subheader("🤖 HF Expert Analysis")
143
+ col1, col2 = st.columns([3, 1])
144
+ with col1:
145
+ st.markdown("""
146
+ **HF Expert Features:**
147
+ - Analyzes entire conversation history
148
+ - Performs web research when needed
149
+ - Provides deep insights and recommendations
150
+ - Acts as expert consultant in your conversation
151
+ """)
152
+ with col2:
153
+ if st.button("🧠 Activate HF Expert",
154
+ key="activate_hf_expert_sidebar",
155
+ help="Send conversation to HF endpoint for deep analysis",
156
+ use_container_width=True,
157
+ disabled=st.session_state.is_processing):
158
+ st.session_state.hf_expert_requested = True
159
 
160
+ # Main interface
161
+ st.title("🧠 AI Life Coach")
162
+ st.markdown("Ask me anything about personal development, goal setting, or life advice!")
163
 
164
+ # Display messages
165
+ for message in st.session_state.messages:
166
+ with st.chat_message(message["role"]):
167
+ # Format HF expert messages differently
168
+ if message.get("source") == "hf_expert":
169
+ st.markdown("### 🤖 HF Expert Analysis")
170
+ st.markdown(message["content"])
171
+ else:
172
+ st.markdown(message["content"])
173
+ if "timestamp" in message:
174
+ st.caption(f"🕒 {message['timestamp']}")
175
 
176
+ # Manual HF Analysis Section
177
+ if st.session_state.messages and len(st.session_state.messages) > 0:
178
+ st.divider()
 
 
 
 
179
 
180
+ # HF Expert Section
181
+ with st.expander("🤖 HF Expert Analysis", expanded=False):
182
+ st.subheader("Deep Conversation Analysis")
 
 
 
 
 
 
183
 
184
+ col1, col2 = st.columns([3, 1])
185
+ with col1:
186
+ st.markdown("""
187
+ **HF Expert Features:**
188
+ - Analyzes entire conversation history
189
+ - Performs web research when needed
190
+ - Provides deep insights and recommendations
191
+ - Acts as expert consultant in your conversation
192
+ """)
193
+
194
+ # Show conversation preview
195
+ st.markdown("**Conversation Preview for HF Expert:**")
196
+ st.markdown("---")
197
+ for i, msg in enumerate(st.session_state.messages[-5:]): # Last 5 messages
198
+ role = "👤 You" if msg["role"] == "user" else "🤖 Assistant"
199
+ st.markdown(f"**{role}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
200
+ st.markdown("---")
201
+
202
+ with col2:
203
+ if st.button("🧠 Activate HF Expert",
204
+ key="activate_hf_expert",
205
+ help="Send conversation to HF endpoint for deep analysis",
206
+ use_container_width=True,
207
+ disabled=st.session_state.is_processing):
208
+ st.session_state.hf_expert_requested = True
209
+
210
+ # Show HF expert analysis when requested
211
+ if st.session_state.get("hf_expert_requested", False):
212
+ with st.spinner("🧠 HF Expert analyzing conversation..."):
213
+ try:
214
+ # Get conversation history
215
+ user_session = session_manager.get_session("default_user")
216
+ conversation_history = user_session.get("conversation", [])
 
 
 
 
 
 
 
 
217
 
218
+ # Show what HF expert sees
219
+ with st.expander("📋 HF Expert Input", expanded=False):
220
+ st.markdown("**Conversation History Sent to HF Expert:**")
221
+ for i, msg in enumerate(conversation_history[-10:]): # Last 10 messages
222
+ st.markdown(f"**{msg['role'].capitalize()}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
 
 
 
 
223
 
224
+ # Request HF analysis
225
+ hf_analysis = coordinator.manual_hf_analysis(
226
+ "default_user",
227
+ conversation_history
228
+ )
 
 
 
229
 
230
+ if hf_analysis:
231
+ # Display HF expert response with clear indication
232
+ with st.chat_message("assistant"):
233
+ st.markdown("### 🤖 HF Expert Analysis")
234
+ st.markdown(hf_analysis)
235
+
236
+ # Add research/web search decisions
237
+ research_needs = coordinator.determine_web_search_needs(conversation_history)
238
+ if research_needs["needs_search"]:
239
+ st.info(f"🔍 **Research Needed:** {research_needs['reasoning']}")
240
+ if st.button("🔎 Perform Web Research", key="web_research_button"):
241
+ # Perform web search
242
+ with st.spinner("🔎 Searching for current information..."):
243
+ # Add web search logic here
244
+ st.success("✅ Web research completed!")
245
+
246
+ # Add to message history with HF expert tag
247
+ st.session_state.messages.append({
248
+ "role": "assistant",
249
+ "content": hf_analysis,
250
+ "timestamp": datetime.now().strftime("%H:%M:%S"),
251
+ "source": "hf_expert",
252
+ "research_needs": research_needs
253
+ })
254
+
255
+ st.session_state.hf_expert_requested = False
256
+
257
+ except Exception as e:
258
+ user_msg = translate_error(e)
259
+ st.error(f"❌ HF Expert analysis failed: {user_msg}")
260
  st.session_state.hf_expert_requested = False
 
 
 
 
 
261
 
262
+ # Chat input - FIXED VERSION
263
+ user_input = st.chat_input("Type your message here...", disabled=st.session_state.is_processing)
264
 
265
+ # Process message when received
266
+ if user_input and not st.session_state.is_processing:
267
+ st.session_state.is_processing = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ # Display user message
270
+ with st.chat_message("user"):
271
+ st.markdown(user_input)
272
+
273
+ st.session_state.messages.append({
274
+ "role": "user",
275
+ "content": user_input,
276
+ "timestamp": datetime.now().strftime("%H:%M:%S")
277
+ })
278
+
279
+ # Process AI response
280
+ with st.chat_message("assistant"):
281
+ response_placeholder = st.empty()
282
+ status_placeholder = st.empty()
283
 
284
  try:
285
+ # Get conversation history
286
+ user_session = session_manager.get_session("default_user")
287
+ conversation = user_session.get("conversation", [])
288
+ conversation_history = conversation[-5:] # Last 5 messages
289
+ conversation_history.append({"role": "user", "content": user_input})
290
+
291
+ # Try Ollama with proper error handling
292
+ status_placeholder.info(PROCESSING_STAGES["ollama"])
293
+ ai_response = None
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  try:
296
+ ai_response = send_to_ollama(
297
+ user_input,
298
+ conversation_history,
299
+ st.session_state.ngrok_url_temp,
300
+ st.session_state.selected_model
301
+ )
302
+
303
  if ai_response:
304
  response_placeholder.markdown(ai_response)
305
+ status_placeholder.success("✅ Response received!")
306
  else:
307
+ status_placeholder.warning("⚠️ Empty response from Ollama")
308
+
309
+ except Exception as ollama_error:
310
+ user_msg = translate_error(ollama_error)
311
  status_placeholder.error(f"⚠️ {user_msg}")
 
 
 
 
 
 
 
 
312
 
313
+ # Fallback to HF if available
314
+ if config.hf_token and not ai_response:
315
+ status_placeholder.info(PROCESSING_STAGES["hf_init"])
316
+ try:
317
+ ai_response = send_to_hf(user_input, conversation_history)
318
+ if ai_response:
319
+ response_placeholder.markdown(ai_response)
320
+ status_placeholder.success("✅ HF response received!")
321
+ else:
322
+ status_placeholder.error("❌ No response from HF")
323
+ except Exception as hf_error:
324
+ user_msg = translate_error(hf_error)
325
+ status_placeholder.error(f"⚠️ {user_msg}")
326
+
327
+ # Save response if successful
328
+ if ai_response:
329
+ # Update conversation history
330
+ conversation.append({"role": "user", "content": user_input})
331
+ conversation.append({"role": "assistant", "content": ai_response})
332
+ user_session["conversation"] = conversation
333
+ session_manager.update_session("default_user", user_session)
334
+
335
+ # Add to message history
336
+ st.session_state.messages.append({
337
+ "role": "assistant",
338
+ "content": ai_response,
339
+ "timestamp": datetime.now().strftime("%H:%M:%S")
340
+ })
341
+ else:
342
+ st.session_state.messages.append({
343
+ "role": "assistant",
344
+ "content": "Sorry, I couldn't process your request. Please try again.",
345
+ "timestamp": datetime.now().strftime("%H:%M:%S")
346
+ })
347
+
348
+ except Exception as e:
349
+ user_msg = translate_error(e)
350
+ response_placeholder.error(f"⚠️ {user_msg}")
351
  st.session_state.messages.append({
352
  "role": "assistant",
353
+ "content": f"⚠️ {user_msg}",
354
  "timestamp": datetime.now().strftime("%H:%M:%S")
355
  })
356
+ finally:
357
+ st.session_state.is_processing = False
358
+ time.sleep(0.5) # Brief pause
359
+ st.experimental_rerun()
360
+
361
+ with tab2:
362
+ st.header("🔬 AI Behavior Evaluator")
363
+ st.markdown("Run sample prompts to observe AI behavior.")
364
+
365
+ eval_prompts = [
366
+ "What is the capital of France?",
367
+ "What day is it today?",
368
+ "Tell me about recent climate policy changes.",
369
+ "Explain CRISPR gene editing simply.",
370
+ "Can vitamin D prevent flu infections?"
371
+ ]
372
+
373
+ selected_prompt = st.selectbox("Choose a test prompt:", eval_prompts)
374
+ custom_prompt = st.text_input("Or enter your own:", "")
375
+
376
+ final_prompt = custom_prompt or selected_prompt
377
+
378
+ if st.button("Evaluate"):
379
+ with st.spinner("Running evaluation..."):
380
+ start_time = time.time()
381
+
382
+ # Simulate sending to coordinator
383
+ from core.session import session_manager
384
+ user_session = session_manager.get_session("eval_user")
385
+ history = user_session.get("conversation", [])
386
+
387
+ try:
388
+ ai_response = send_to_ollama(final_prompt, history, st.session_state.ngrok_url_temp, st.session_state.selected_model)
389
+ duration = round(time.time() - start_time, 2)
390
+
391
+ st.success(f"✅ Response generated in {duration}s")
392
+ st.markdown("**Response:**")
393
+ st.write(ai_response)
394
+
395
+ st.markdown("**Analysis Tags:**")
396
+ tags = []
397
+ if "today" in final_prompt.lower() or "date" in final_prompt.lower():
398
+ tags.append("📅 Date Awareness")
399
+ if any(word in final_prompt.lower() for word in ["news", "latest", "breaking"]):
400
+ tags.append("🌐 Web Search Needed")
401
+ if any(word in final_prompt.lower() for word in ["vitamin", "drug", "metformin", "CRISPR"]):
402
+ tags.append("🧬 Scientific Knowledge")
403
+ st.write(", ".join(tags) if tags else "General Knowledge")
404
+
405
+ # Show research papers if scientific topic
406
+ research_keywords = ["study", "research", "paper", "effectiveness", "clinical trial", "vitamin", "drug", "metformin", "CRISPR"]
407
+ if any(kw in final_prompt.lower() for kw in research_keywords):
408
+ st.markdown("**Related Research Papers:**")
409
+ with st.spinner("Searching for academic papers..."):
410
+ try:
411
+ papers = find_papers(final_prompt, limit=3)
412
+ if papers:
413
+ for i, paper in enumerate(papers):
414
+ with st.expander(f"📄 {paper['title'][:50]}{'...' if len(paper['title']) > 50 else ''}"):
415
+ st.markdown(f"**Authors:** {', '.join(paper['authors'][:3])}")
416
+ st.markdown(f"**Year:** {paper['year']}")
417
+ st.markdown(f"**Citations:** {paper['citation_count']}")
418
+ st.markdown(f"**Venue:** {paper['venue']}")
419
+ st.markdown(f"**Abstract:** {paper['abstract'][:300]}{'...' if len(paper['abstract']) > 300 else ''}")
420
+ st.markdown(f"[View Paper]({paper['url']})")
421
+ else:
422
+ st.info("No relevant papers found for this topic.")
423
+ except Exception as e:
424
+ st.warning(f"Could not fetch research papers: {translate_error(e)}")
425
+
426
+ except Exception as e:
427
+ st.error(f"Evaluation failed: {translate_error(e)}")
428
+
429
+ with tab3:
430
+ st.header("📊 Performance Reports")
431
+ st.markdown("System performance metrics and usage analytics.")
432
+
433
+ # System status
434
+ st.subheader("System Status")
435
+ col1, col2, col3 = st.columns(3)
436
+
437
+ with col1:
438
+ try:
439
+ from services.ollama_monitor import check_ollama_status
440
+ ollama_status = check_ollama_status()
441
+ if ollama_status.get("running"):
442
+ st.success("🦙 Ollama: Running")
443
  else:
444
+ st.warning("🦙 Ollama: Not running")
445
+ except:
446
+ st.info("🦙 Ollama: Unknown")
447
+
448
+ with col2:
449
+ try:
450
+ from services.hf_endpoint_monitor import hf_monitor
451
+ hf_status = hf_monitor.check_endpoint_status()
452
+ if hf_status['available']:
453
+ st.success("🤗 HF: Available")
454
+ else:
455
+ st.warning("🤗 HF: Not available")
456
+ except:
457
+ st.info("🤗 HF: Unknown")
458
+
459
+ with col3:
460
+ if check_redis_health():
461
+ st.success("💾 Redis: Connected")
462
+ else:
463
+ st.error("💾 Redis: Disconnected")
464
+
465
+ # Session statistics
466
+ st.subheader("Session Statistics")
467
+ try:
468
+ user_session = session_manager.get_session("default_user")
469
+ conversation = user_session.get("conversation", [])
470
+ st.metric("Total Messages", len(conversation))
471
 
472
+ coord_stats = user_session.get('ai_coordination', {})
473
+ if coord_stats:
474
+ st.metric("AI Requests Processed", coord_stats.get('requests_processed', 0))
475
+ st.metric("Ollama Responses", coord_stats.get('ollama_responses', 0))
476
+ st.metric("HF Responses", coord_stats.get('hf_responses', 0))
477
+ else:
478
+ st.info("No coordination statistics available yet.")
479
+ except Exception as e:
480
+ st.warning(f"Could not load session statistics: {translate_error(e)}")
481
+
482
+ # Recent activity
483
+ st.subheader("Recent Activity")
484
+ try:
485
+ recent_activities = coordinator.get_recent_activities("default_user")
486
+ if recent_activities and recent_activities.get('last_request'):
487
+ st.markdown(f"**Last Request:** {recent_activities['last_request']}")
488
+ st.markdown(f"**Requests Processed:** {recent_activities['requests_processed']}")
489
+ st.markdown(f"**Ollama Responses:** {recent_activities['ollama_responses']}")
490
+ st.markdown(f"**HF Responses:** {recent_activities['hf_responses']}")
491
+ else:
492
+ st.info("No recent activity recorded.")
493
+ except Exception as e:
494
+ st.warning(f"Could not load recent activity: {translate_error(e)}")
495
+
496
+ # Configuration summary
497
+ st.subheader("Configuration Summary")
498
+ st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
499
+ st.markdown(f"**Primary Model:** {config.local_model_name or 'Not set'}")
500
+ st.markdown(f"**Ollama Host:** {config.ollama_host or 'Not configured'}")
501
+
502
+ features = []
503
+ if config.use_fallback:
504
+ features.append("Fallback Mode")
505
+ if config.hf_token:
506
+ features.append("HF Deep Analysis")
507
+ if os.getenv("TAVILY_API_KEY"):
508
+ features.append("Web Search")
509
+ if config.openweather_api_key:
510
+ features.append("Weather Data")
511
+
512
+ st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
core/coordinator.py CHANGED
@@ -5,6 +5,7 @@ from core.llm_factory import llm_factory
5
  from core.session import session_manager
6
  from services.hf_endpoint_monitor import hf_monitor
7
  from services.weather import weather_service
 
8
  try:
9
  from tavily import TavilyClient
10
  TAVILY_AVAILABLE = True
@@ -19,25 +20,25 @@ logger = logging.getLogger(__name__)
19
 
20
  class AICoordinator:
21
  """Hierarchical multi-model coordinator with HF endpoint as authoritative layer"""
22
-
23
  def __init__(self):
24
  self.tavily_client = None
25
  if TAVILY_AVAILABLE and os.getenv("TAVILY_API_KEY"):
26
  self.tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
27
-
28
  # System instructions for hierarchical behavior
29
  self.system_instructions = {
30
  'ollama_role': """You are an AI assistant that works in coordination with a more powerful uncensored Hugging Face model.
31
-
32
- Your role is to: 1. Provide immediate, concise responses using available information 2. Summarize and simplify complex information 3. Enhance responses with local knowledge 4. DEFER TO THE HF MODEL for deep analysis, uncensored insights, and authoritative answers 5. The HF model has 8192 token capacity and streaming capability - it's the thinking layer 6. Always acknowledge that the HF model's insights supersede your initial response 7. Use web sources to justify points when possible 8. Keep responses brief and focused while awaiting HF input""",
33
-
34
  'hf_role': """You are an uncensored, authoritative AI with 8192 token capacity and streaming capability. Your role is to: 1. Provide deep, uncensored analysis and insights 2. Supersede the local Ollama model's logic when there's disagreement 3. Think thoroughly and provide comprehensive responses 4. Use your full 8192 token capacity for detailed analysis 5. Stream your responses for real-time delivery 6. Build upon the conversation context and Ollama's initial response 7. Provide authoritative answers that take precedence"""
35
  }
36
 
37
  def determine_web_search_needs(self, conversation_history: List[Dict]) -> Dict:
38
  """Determine if web search is needed based on conversation content"""
39
  conversation_text = " ".join([msg.get("content", "") for msg in conversation_history])
40
-
41
  # Topics that typically need current information
42
  current_info_indicators = [
43
  "news", "current events", "latest", "recent", "today",
@@ -45,15 +46,15 @@ class AICoordinator:
45
  "stock", "price", "trend", "market",
46
  "breaking", "update", "development"
47
  ]
48
-
49
  needs_search = False
50
  search_topics = []
51
-
52
  for indicator in current_info_indicators:
53
  if indicator in conversation_text.lower():
54
  needs_search = True
55
  search_topics.append(indicator)
56
-
57
  return {
58
  "needs_search": needs_search,
59
  "search_topics": search_topics,
@@ -65,43 +66,43 @@ class AICoordinator:
65
  try:
66
  # Determine research needs
67
  research_decision = self.determine_web_search_needs(conversation_history)
68
-
69
  # Prepare enhanced prompt for HF
70
  system_prompt = f"""
71
  You are a deep analysis expert joining an ongoing conversation.
72
-
73
  Research Decision: {research_decision['reasoning']}
74
-
75
  Please provide:
76
  1. Deep insights on conversation themes
77
  2. Research/web search needs (if any)
78
  3. Strategic recommendations
79
  4. Questions to explore further
80
-
81
  Conversation History:
82
  """
83
-
84
  # Add conversation history to messages
85
  messages = [{"role": "system", "content": system_prompt}]
86
-
87
  # Add recent conversation (last 15 messages for context)
88
  for msg in conversation_history[-15:]:
89
  messages.append({
90
  "role": msg["role"],
91
  "content": msg["content"]
92
  })
93
-
94
  # Get HF provider
95
  from core.llm_factory import llm_factory
96
  hf_provider = llm_factory.get_provider('huggingface')
97
-
98
  if hf_provider:
99
  # Generate deep analysis with full 8192 token capacity
100
  response = hf_provider.generate("Deep analysis request", messages)
101
  return response or "HF Expert analysis completed."
102
  else:
103
  return "❌ HF provider not available."
104
-
105
  except Exception as e:
106
  return f"❌ HF analysis failed: {str(e)}"
107
 
@@ -111,7 +112,7 @@ class AICoordinator:
111
  return {
112
  "hf_available": self._check_hf_availability(),
113
  "web_search_configured": bool(self.tavily_client),
114
- "research_needs_detected": False, # Will be determined per conversation
115
  "last_hf_analysis": None # Track last analysis time
116
  }
117
 
@@ -123,7 +124,7 @@ class AICoordinator:
123
  # Get conversation history
124
  session = session_manager.get_session(user_id)
125
  conversation_history = session.get("conversation", []).copy()
126
-
127
  yield {
128
  'type': 'coordination_status',
129
  'content': '🚀 Initiating hierarchical AI coordination...',
@@ -132,7 +133,7 @@ class AICoordinator:
132
  'user_query_length': len(user_query)
133
  }
134
  }
135
-
136
  # Step 1: Gather external data with detailed logging
137
  yield {
138
  'type': 'coordination_status',
@@ -140,7 +141,7 @@ class AICoordinator:
140
  'details': {'phase': 'external_data_gathering'}
141
  }
142
  external_data = await self._gather_external_data(user_query)
143
-
144
  # Log what external data was gathered
145
  if external_data:
146
  data_summary = []
@@ -150,13 +151,13 @@ class AICoordinator:
150
  data_summary.append("Weather data: available")
151
  if 'current_datetime' in external_data:
152
  data_summary.append(f"Time: {external_data['current_datetime']}")
153
-
154
  yield {
155
  'type': 'coordination_status',
156
  'content': f'📊 External data gathered: {", ".join(data_summary)}',
157
  'details': {'external_data_summary': data_summary}
158
  }
159
-
160
  # Step 2: Get initial Ollama response
161
  yield {
162
  'type': 'coordination_status',
@@ -166,7 +167,7 @@ class AICoordinator:
166
  ollama_response = await self._get_hierarchical_ollama_response(
167
  user_query, conversation_history, external_data
168
  )
169
-
170
  # Send initial response with context info
171
  yield {
172
  'type': 'initial_response',
@@ -176,14 +177,14 @@ class AICoordinator:
176
  'external_data_injected': bool(external_data)
177
  }
178
  }
179
-
180
  # Step 3: Coordinate with HF endpoint
181
  yield {
182
  'type': 'coordination_status',
183
  'content': '🤗 Engaging HF endpoint for deep analysis...',
184
  'details': {'phase': 'hf_coordination'}
185
  }
186
-
187
  # Check HF availability
188
  hf_available = self._check_hf_availability()
189
  if hf_available:
@@ -193,13 +194,13 @@ class AICoordinator:
193
  'ollama_response_length': len(ollama_response),
194
  'external_data_items': len(external_data) if external_data else 0
195
  }
196
-
197
  yield {
198
  'type': 'coordination_status',
199
  'content': f'📋 HF context: {len(conversation_history)} conversation turns, Ollama response ({len(ollama_response)} chars)',
200
  'details': context_summary
201
  }
202
-
203
  # Coordinate with HF
204
  async for hf_chunk in self._coordinate_hierarchical_hf_response(
205
  user_id, user_query, conversation_history,
@@ -212,14 +213,14 @@ class AICoordinator:
212
  'content': 'ℹ️ HF endpoint not available - using Ollama response',
213
  'details': {'hf_available': False}
214
  }
215
-
216
  # Final coordination status
217
  yield {
218
  'type': 'coordination_status',
219
  'content': '✅ Hierarchical coordination complete',
220
  'details': {'status': 'complete'}
221
  }
222
-
223
  except Exception as e:
224
  logger.error(f"Hierarchical coordination failed: {e}")
225
  yield {
@@ -235,29 +236,29 @@ class AICoordinator:
235
  try:
236
  # Check and warm up HF endpoint if needed
237
  hf_status = hf_monitor.check_endpoint_status()
238
-
239
  if not hf_status['available']:
240
  yield {'type': 'coordination_status', 'content': '⚡ Initializing HF endpoint (2-4 minutes)...'}
241
  warmup_success = hf_monitor.handle_scale_to_zero()
242
  if not warmup_success:
243
  yield {'type': 'coordination_status', 'content': '❌ HF endpoint initialization failed'}
244
  return
245
-
246
  # Get HF provider
247
  hf_provider = llm_factory.get_provider('huggingface')
248
  if not hf_provider:
249
  yield {'type': 'coordination_status', 'content': '❌ HF provider not available'}
250
  return
251
-
252
  # Prepare enhanced conversation for HF with hierarchical context
253
  enhanced_history = history.copy()
254
-
255
  # Add system instructions for HF
256
  enhanced_history.insert(0, {
257
  "role": "system",
258
  "content": self.system_instructions['hf_role']
259
  })
260
-
261
  # Add context about the coordination
262
  enhanced_history.append({
263
  "role": "system",
@@ -265,21 +266,21 @@ class AICoordinator:
265
  User Query: '{query}'
266
  Ollama's Initial Response: '{ollama_response}'
267
  External Data Available: {json.dumps(external_data, indent=2) if external_data else 'None'}
268
-
269
  Please provide your deep, uncensored analysis that supersedes the local model's response.
270
  Use your full 8192 token capacity for comprehensive thinking.
271
  Stream your response for real-time delivery."""
272
  })
273
-
274
  # Add the user's latest query
275
  enhanced_history.append({"role": "user", "content": query})
276
-
277
  # Stream HF response with full 8192 token capacity
278
  yield {'type': 'coordination_status', 'content': '🧠 HF endpoint thinking...'}
279
-
280
  # Use streaming for real-time delivery
281
  hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
282
-
283
  if hf_response_stream:
284
  # Stream the response chunks
285
  full_hf_response = ""
@@ -287,7 +288,7 @@ class AICoordinator:
287
  if chunk:
288
  full_hf_response += chunk
289
  yield {'type': 'hf_thinking', 'content': chunk}
290
-
291
  # Final HF response
292
  yield {'type': 'final_response', 'content': full_hf_response}
293
  yield {'type': 'coordination_status', 'content': '🎯 HF analysis complete and authoritative'}
@@ -305,16 +306,16 @@ class AICoordinator:
305
  ollama_provider = llm_factory.get_provider('ollama')
306
  if not ollama_provider:
307
  raise Exception("Ollama provider not available")
308
-
309
  # Prepare conversation with hierarchical context
310
  enhanced_history = history.copy()
311
-
312
  # Add system instruction for Ollama's role
313
  enhanced_history.insert(0, {
314
  "role": "system",
315
  "content": self.system_instructions['ollama_role']
316
  })
317
-
318
  # Add external data context if available
319
  if external_data:
320
  context_parts = []
@@ -325,26 +326,26 @@ class AICoordinator:
325
  context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}°C in {weather.get('city', 'Unknown')}")
326
  if 'current_datetime' in external_data:
327
  context_parts.append(f"Current time: {external_data['current_datetime']}")
328
-
329
  if context_parts:
330
  context_message = {
331
  "role": "system",
332
  "content": "Context: " + " | ".join(context_parts)
333
  }
334
  enhanced_history.insert(1, context_message) # Insert after role instruction
335
-
336
  # Add the user's query
337
  enhanced_history.append({"role": "user", "content": query})
338
-
339
  # Generate response with awareness of HF's superior capabilities
340
  response = ollama_provider.generate(query, enhanced_history)
341
-
342
  # Add acknowledgment of HF's authority
343
  if response:
344
  return f"{response}\n\n*Note: A more comprehensive analysis from the uncensored HF model is being prepared...*"
345
  else:
346
  return "I'm processing your request... A deeper analysis is being prepared by the authoritative model."
347
-
348
  except Exception as e:
349
  logger.error(f"Hierarchical Ollama response failed: {e}")
350
  return "I'm thinking about your question... Preparing a comprehensive response."
@@ -360,24 +361,18 @@ class AICoordinator:
360
  async def _gather_external_data(self, query: str) -> Dict:
361
  """Gather external data from various sources"""
362
  data = {}
363
-
364
  # Tavily/DuckDuckGo search with justification focus
365
- if self.tavily_client:
366
  try:
367
- search_result = self.tavily_client.search(
368
- f"current information about {query}",
369
- max_results=5, # More results for better justification
370
- include_answer=True,
371
- include_raw_content=True # For deeper analysis
372
- )
373
- data['search_results'] = search_result.get('results', [])
374
- if search_result.get('answer'):
375
- data['search_answer'] = search_result['answer']
376
- # Store raw content for HF to analyze
377
- data['raw_sources'] = [result.get('raw_content', '')[:1000] for result in search_result.get('results', [])[:3]]
378
  except Exception as e:
379
  logger.warning(f"Tavily search failed: {e}")
380
-
381
  # Weather data
382
  weather_keywords = ['weather', 'temperature', 'forecast', 'climate', 'rain', 'sunny']
383
  if any(keyword in query.lower() for keyword in weather_keywords):
@@ -388,10 +383,10 @@ class AICoordinator:
388
  data['weather'] = weather
389
  except Exception as e:
390
  logger.warning(f"Weather data failed: {e}")
391
-
392
  # Current date/time
393
  data['current_datetime'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
394
-
395
  return data
396
 
397
  def _extract_location(self, query: str) -> Optional[str]:
 
5
  from core.session import session_manager
6
  from services.hf_endpoint_monitor import hf_monitor
7
  from services.weather import weather_service
8
+ from services.web_search import web_search_service
9
  try:
10
  from tavily import TavilyClient
11
  TAVILY_AVAILABLE = True
 
20
 
21
  class AICoordinator:
22
  """Hierarchical multi-model coordinator with HF endpoint as authoritative layer"""
23
+
24
  def __init__(self):
25
  self.tavily_client = None
26
  if TAVILY_AVAILABLE and os.getenv("TAVILY_API_KEY"):
27
  self.tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
28
+
29
  # System instructions for hierarchical behavior
30
  self.system_instructions = {
31
  'ollama_role': """You are an AI assistant that works in coordination with a more powerful uncensored Hugging Face model.
32
+
33
+ Your role is to: 1. Provide immediate, concise responses using available information 2. Summarize and simplify complex information 3. Enhance responses with local knowledge 4. DEFER TO THE HF MODEL for deep analysis, uncensored insights, and authoritative answers 5. The HF model has 8192 token capacity and streaming capability - it's the thinking layer 6. Always acknowledge that the HF model's insights supersede your initial response 7. Use web sources to justify points when possible 8. Keep responses brief and focused while awaiting HF input""",
34
+
35
  'hf_role': """You are an uncensored, authoritative AI with 8192 token capacity and streaming capability. Your role is to: 1. Provide deep, uncensored analysis and insights 2. Supersede the local Ollama model's logic when there's disagreement 3. Think thoroughly and provide comprehensive responses 4. Use your full 8192 token capacity for detailed analysis 5. Stream your responses for real-time delivery 6. Build upon the conversation context and Ollama's initial response 7. Provide authoritative answers that take precedence"""
36
  }
37
 
38
  def determine_web_search_needs(self, conversation_history: List[Dict]) -> Dict:
39
  """Determine if web search is needed based on conversation content"""
40
  conversation_text = " ".join([msg.get("content", "") for msg in conversation_history])
41
+
42
  # Topics that typically need current information
43
  current_info_indicators = [
44
  "news", "current events", "latest", "recent", "today",
 
46
  "stock", "price", "trend", "market",
47
  "breaking", "update", "development"
48
  ]
49
+
50
  needs_search = False
51
  search_topics = []
52
+
53
  for indicator in current_info_indicators:
54
  if indicator in conversation_text.lower():
55
  needs_search = True
56
  search_topics.append(indicator)
57
+
58
  return {
59
  "needs_search": needs_search,
60
  "search_topics": search_topics,
 
66
  try:
67
  # Determine research needs
68
  research_decision = self.determine_web_search_needs(conversation_history)
69
+
70
  # Prepare enhanced prompt for HF
71
  system_prompt = f"""
72
  You are a deep analysis expert joining an ongoing conversation.
73
+
74
  Research Decision: {research_decision['reasoning']}
75
+
76
  Please provide:
77
  1. Deep insights on conversation themes
78
  2. Research/web search needs (if any)
79
  3. Strategic recommendations
80
  4. Questions to explore further
81
+
82
  Conversation History:
83
  """
84
+
85
  # Add conversation history to messages
86
  messages = [{"role": "system", "content": system_prompt}]
87
+
88
  # Add recent conversation (last 15 messages for context)
89
  for msg in conversation_history[-15:]:
90
  messages.append({
91
  "role": msg["role"],
92
  "content": msg["content"]
93
  })
94
+
95
  # Get HF provider
96
  from core.llm_factory import llm_factory
97
  hf_provider = llm_factory.get_provider('huggingface')
98
+
99
  if hf_provider:
100
  # Generate deep analysis with full 8192 token capacity
101
  response = hf_provider.generate("Deep analysis request", messages)
102
  return response or "HF Expert analysis completed."
103
  else:
104
  return "❌ HF provider not available."
105
+
106
  except Exception as e:
107
  return f"❌ HF analysis failed: {str(e)}"
108
 
 
112
  return {
113
  "hf_available": self._check_hf_availability(),
114
  "web_search_configured": bool(self.tavily_client),
115
+ "research_needs_detected": False, # Will be determined per conversation,
116
  "last_hf_analysis": None # Track last analysis time
117
  }
118
 
 
124
  # Get conversation history
125
  session = session_manager.get_session(user_id)
126
  conversation_history = session.get("conversation", []).copy()
127
+
128
  yield {
129
  'type': 'coordination_status',
130
  'content': '🚀 Initiating hierarchical AI coordination...',
 
133
  'user_query_length': len(user_query)
134
  }
135
  }
136
+
137
  # Step 1: Gather external data with detailed logging
138
  yield {
139
  'type': 'coordination_status',
 
141
  'details': {'phase': 'external_data_gathering'}
142
  }
143
  external_data = await self._gather_external_data(user_query)
144
+
145
  # Log what external data was gathered
146
  if external_data:
147
  data_summary = []
 
151
  data_summary.append("Weather data: available")
152
  if 'current_datetime' in external_data:
153
  data_summary.append(f"Time: {external_data['current_datetime']}")
154
+
155
  yield {
156
  'type': 'coordination_status',
157
  'content': f'📊 External data gathered: {", ".join(data_summary)}',
158
  'details': {'external_data_summary': data_summary}
159
  }
160
+
161
  # Step 2: Get initial Ollama response
162
  yield {
163
  'type': 'coordination_status',
 
167
  ollama_response = await self._get_hierarchical_ollama_response(
168
  user_query, conversation_history, external_data
169
  )
170
+
171
  # Send initial response with context info
172
  yield {
173
  'type': 'initial_response',
 
177
  'external_data_injected': bool(external_data)
178
  }
179
  }
180
+
181
  # Step 3: Coordinate with HF endpoint
182
  yield {
183
  'type': 'coordination_status',
184
  'content': '🤗 Engaging HF endpoint for deep analysis...',
185
  'details': {'phase': 'hf_coordination'}
186
  }
187
+
188
  # Check HF availability
189
  hf_available = self._check_hf_availability()
190
  if hf_available:
 
194
  'ollama_response_length': len(ollama_response),
195
  'external_data_items': len(external_data) if external_data else 0
196
  }
197
+
198
  yield {
199
  'type': 'coordination_status',
200
  'content': f'📋 HF context: {len(conversation_history)} conversation turns, Ollama response ({len(ollama_response)} chars)',
201
  'details': context_summary
202
  }
203
+
204
  # Coordinate with HF
205
  async for hf_chunk in self._coordinate_hierarchical_hf_response(
206
  user_id, user_query, conversation_history,
 
213
  'content': 'ℹ️ HF endpoint not available - using Ollama response',
214
  'details': {'hf_available': False}
215
  }
216
+
217
  # Final coordination status
218
  yield {
219
  'type': 'coordination_status',
220
  'content': '✅ Hierarchical coordination complete',
221
  'details': {'status': 'complete'}
222
  }
223
+
224
  except Exception as e:
225
  logger.error(f"Hierarchical coordination failed: {e}")
226
  yield {
 
236
  try:
237
  # Check and warm up HF endpoint if needed
238
  hf_status = hf_monitor.check_endpoint_status()
239
+
240
  if not hf_status['available']:
241
  yield {'type': 'coordination_status', 'content': '⚡ Initializing HF endpoint (2-4 minutes)...'}
242
  warmup_success = hf_monitor.handle_scale_to_zero()
243
  if not warmup_success:
244
  yield {'type': 'coordination_status', 'content': '❌ HF endpoint initialization failed'}
245
  return
246
+
247
  # Get HF provider
248
  hf_provider = llm_factory.get_provider('huggingface')
249
  if not hf_provider:
250
  yield {'type': 'coordination_status', 'content': '❌ HF provider not available'}
251
  return
252
+
253
  # Prepare enhanced conversation for HF with hierarchical context
254
  enhanced_history = history.copy()
255
+
256
  # Add system instructions for HF
257
  enhanced_history.insert(0, {
258
  "role": "system",
259
  "content": self.system_instructions['hf_role']
260
  })
261
+
262
  # Add context about the coordination
263
  enhanced_history.append({
264
  "role": "system",
 
266
  User Query: '{query}'
267
  Ollama's Initial Response: '{ollama_response}'
268
  External Data Available: {json.dumps(external_data, indent=2) if external_data else 'None'}
269
+
270
  Please provide your deep, uncensored analysis that supersedes the local model's response.
271
  Use your full 8192 token capacity for comprehensive thinking.
272
  Stream your response for real-time delivery."""
273
  })
274
+
275
  # Add the user's latest query
276
  enhanced_history.append({"role": "user", "content": query})
277
+
278
  # Stream HF response with full 8192 token capacity
279
  yield {'type': 'coordination_status', 'content': '🧠 HF endpoint thinking...'}
280
+
281
  # Use streaming for real-time delivery
282
  hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
283
+
284
  if hf_response_stream:
285
  # Stream the response chunks
286
  full_hf_response = ""
 
288
  if chunk:
289
  full_hf_response += chunk
290
  yield {'type': 'hf_thinking', 'content': chunk}
291
+
292
  # Final HF response
293
  yield {'type': 'final_response', 'content': full_hf_response}
294
  yield {'type': 'coordination_status', 'content': '🎯 HF analysis complete and authoritative'}
 
306
  ollama_provider = llm_factory.get_provider('ollama')
307
  if not ollama_provider:
308
  raise Exception("Ollama provider not available")
309
+
310
  # Prepare conversation with hierarchical context
311
  enhanced_history = history.copy()
312
+
313
  # Add system instruction for Ollama's role
314
  enhanced_history.insert(0, {
315
  "role": "system",
316
  "content": self.system_instructions['ollama_role']
317
  })
318
+
319
  # Add external data context if available
320
  if external_data:
321
  context_parts = []
 
326
  context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}°C in {weather.get('city', 'Unknown')}")
327
  if 'current_datetime' in external_data:
328
  context_parts.append(f"Current time: {external_data['current_datetime']}")
329
+
330
  if context_parts:
331
  context_message = {
332
  "role": "system",
333
  "content": "Context: " + " | ".join(context_parts)
334
  }
335
  enhanced_history.insert(1, context_message) # Insert after role instruction
336
+
337
  # Add the user's query
338
  enhanced_history.append({"role": "user", "content": query})
339
+
340
  # Generate response with awareness of HF's superior capabilities
341
  response = ollama_provider.generate(query, enhanced_history)
342
+
343
  # Add acknowledgment of HF's authority
344
  if response:
345
  return f"{response}\n\n*Note: A more comprehensive analysis from the uncensored HF model is being prepared...*"
346
  else:
347
  return "I'm processing your request... A deeper analysis is being prepared by the authoritative model."
348
+
349
  except Exception as e:
350
  logger.error(f"Hierarchical Ollama response failed: {e}")
351
  return "I'm thinking about your question... Preparing a comprehensive response."
 
361
  async def _gather_external_data(self, query: str) -> Dict:
362
  """Gather external data from various sources"""
363
  data = {}
364
+
365
  # Tavily/DuckDuckGo search with justification focus
366
+ if self.tavily_client or web_search_service.client:
367
  try:
368
+ search_results = web_search_service.search(f"current information about {query}")
369
+ if search_results:
370
+ data['search_results'] = search_results
371
+ # Optionally extract answer summary
372
+ # data['search_answer'] = ...
 
 
 
 
 
 
373
  except Exception as e:
374
  logger.warning(f"Tavily search failed: {e}")
375
+
376
  # Weather data
377
  weather_keywords = ['weather', 'temperature', 'forecast', 'climate', 'rain', 'sunny']
378
  if any(keyword in query.lower() for keyword in weather_keywords):
 
383
  data['weather'] = weather
384
  except Exception as e:
385
  logger.warning(f"Weather data failed: {e}")
386
+
387
  # Current date/time
388
  data['current_datetime'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
389
+
390
  return data
391
 
392
  def _extract_location(self, query: str) -> Optional[str]:
requirements.txt CHANGED
@@ -10,3 +10,4 @@ docker==6.1.3
10
  pygame==2.5.2
11
  pydantic==1.10.7
12
  typing-extensions>=4.5.0
 
 
10
  pygame==2.5.2
11
  pydantic==1.10.7
12
  typing-extensions>=4.5.0
13
+ semanticscholar>=0.1.8
services/research/papers.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from semanticscholar import SemanticScholar
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+ scholar = SemanticScholar(timeout=10)
7
+
8
+ def find_papers(query: str, limit: int = 5):
9
+ """
10
+ Search academic papers via Semantic Scholar API.
11
+ Returns simplified paper metadata.
12
+ """
13
+ try:
14
+ results = scholar.search_paper(query, limit=limit)
15
+ papers = []
16
+ for paper in results:
17
+ papers.append({
18
+ 'title': paper.title,
19
+ 'authors': [author.name for author in paper.authors],
20
+ 'abstract': paper.abstract,
21
+ 'year': paper.year,
22
+ 'url': paper.url,
23
+ 'citation_count': getattr(paper, 'citationCount', 0),
24
+ 'venue': getattr(paper, 'venue', '')
25
+ })
26
+ return papers
27
+ except Exception as e:
28
+ logger.error(f"Paper search failed: {e}")
29
+ return []
30
+
31
+ # Example usage:
32
+ # papers = find_papers("vitamin D immune system")
services/web_search.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Optional, Dict, List
3
+ try:
4
+ from tavily import TavilyClient
5
+ TAVILY_AVAILABLE = True
6
+ except ImportError:
7
+ TavilyClient = None
8
+ TAVILY_AVAILABLE = False
9
+
10
+ class WebSearchService:
11
+ def __init__(self):
12
+ self.client = None
13
+ api_key = os.getenv("TAVILY_API_KEY")
14
+ if api_key and TAVILY_AVAILABLE:
15
+ self.client = TavilyClient(api_key=api_key)
16
+
17
+ def search(self, query: str, max_results: int = 5) -> Optional[List[Dict]]:
18
+ """
19
+ Perform a web search using Tavily.
20
+ Returns list of results with title, url, content.
21
+ """
22
+ if not self.client:
23
+ print("⚠️ Tavily API key not configured.")
24
+ return []
25
+
26
+ try:
27
+ response = self.client.search(
28
+ query=query,
29
+ max_results=max_results,
30
+ include_answer=True,
31
+ include_raw_content=False
32
+ )
33
+ return response.get("results", [])
34
+ except Exception as e:
35
+ print(f"❌ Web search failed: {e}")
36
+ return []
37
+
38
+ # Global instance
39
+ web_search_service = WebSearchService()