rdune71 commited on
Commit
bed2d0a
·
1 Parent(s): abbb601

Enhance CosmicCat AI with space-themed personality, NASA integration, and improved UI

Browse files
Files changed (4) hide show
  1. app.py +82 -136
  2. core/personality.py +84 -0
  3. core/providers/ollama.py +13 -18
  4. services/nasa_api.py +78 -0
app.py CHANGED
@@ -14,6 +14,9 @@ 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
@@ -31,10 +34,18 @@ if "is_processing" not in st.session_state:
31
  st.session_state.is_processing = False
32
  if "ngrok_url_temp" not in st.session_state:
33
  st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
34
- if "hf_expert_requested" not in st.session_state:
35
- st.session_state.hf_expert_requested = False
36
  if "cosmic_mode" not in st.session_state:
37
  st.session_state.cosmic_mode = True # Default to cosmic mode
 
 
 
 
 
 
 
 
 
 
38
 
39
  # Sidebar layout redesign
40
  with st.sidebar:
@@ -56,8 +67,8 @@ with st.sidebar:
56
  )
57
  st.session_state.selected_model = model_options[selected_model_name]
58
 
59
- # Toggle for cosmic mode using checkbox instead of toggle
60
- st.session_state.cosmic_mode = st.checkbox("Enable Cosmic Cascade", value=st.session_state.cosmic_mode)
61
 
62
  st.divider()
63
 
@@ -100,8 +111,8 @@ with st.sidebar:
100
 
101
  st.divider()
102
 
103
- # ADVANCED FEATURES
104
- with st.expander("🔍 Advanced Features", expanded=False):
105
  st.subheader("📊 System Monitor")
106
  try:
107
  from services.ollama_monitor import check_ollama_status
@@ -114,12 +125,14 @@ with st.sidebar:
114
  st.info("🦙 Ollama: Unknown")
115
 
116
  try:
117
- from services.hf_endpoint_monitor import hf_monitor
118
  hf_status = hf_monitor.check_endpoint_status()
119
  if hf_status['available']:
120
- st.success("🤗 HF: Available")
 
 
 
121
  else:
122
- st.warning("🤗 HF: Not available")
123
  except:
124
  st.info("🤗 HF: Unknown")
125
 
@@ -130,27 +143,20 @@ with st.sidebar:
130
 
131
  st.divider()
132
 
133
- st.subheader("🤖 HF Expert Analysis")
134
- st.markdown("""
135
- **HF Expert Features:**
136
- - Analyzes entire conversation history
137
- - Performs web research when needed
138
- - Provides deep insights and recommendations
139
- - Acts as expert consultant in your conversation
140
- """)
141
- if st.button("🧠 Activate HF Expert",
142
- key="activate_hf_expert_sidebar",
143
- help="Send conversation to HF endpoint for deep analysis",
144
- use_container_width=True,
145
- disabled=st.session_state.is_processing):
146
- st.session_state.hf_expert_requested = True
147
 
148
  st.divider()
149
  st.subheader("🐛 Debug Info")
150
  # Show current configuration
151
  st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
152
  st.markdown(f"**Model:** {st.session_state.selected_model}")
153
- st.markdown(f"**Ollama URL:** {st.session_state.ngrok_url_temp}")
154
  st.markdown(f"**Cosmic Mode:** {'Enabled' if st.session_state.cosmic_mode else 'Disabled'}")
155
 
156
  # Show active features
@@ -161,6 +167,8 @@ with st.sidebar:
161
  features.append("Web Search")
162
  if config.openweather_api_key:
163
  features.append("Weather")
 
 
164
 
165
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
166
 
@@ -168,6 +176,16 @@ with st.sidebar:
168
  st.title("🐱 CosmicCat AI Assistant")
169
  st.markdown("Ask me anything about personal development, goal setting, or life advice!")
170
 
 
 
 
 
 
 
 
 
 
 
171
  # Consistent message rendering function with cosmic styling
172
  def render_message(role, content, source=None, timestamp=None):
173
  """Render chat messages with consistent styling"""
@@ -181,8 +199,8 @@ def render_message(role, content, source=None, timestamp=None):
181
  st.markdown(f"### 🌟 Final Cosmic Summary:")
182
  elif source == "error":
183
  st.markdown(f"### ❌ Error:")
184
- elif source == "hf_expert":
185
- st.markdown(f"### 🤖 HF Expert Analysis:")
186
  else:
187
  st.markdown(f"### {source}")
188
 
@@ -199,105 +217,6 @@ for message in st.session_state.messages:
199
  message.get("timestamp")
200
  )
201
 
202
- # Manual HF Analysis Section
203
- if st.session_state.messages and len(st.session_state.messages) > 0:
204
- st.divider()
205
-
206
- # HF Expert Section with enhanced visual indication
207
- with st.expander("🤖 HF Expert Analysis", expanded=False):
208
- st.subheader("Deep Conversation Analysis")
209
-
210
- col1, col2 = st.columns([3, 1])
211
- with col1:
212
- st.markdown("""
213
- **HF Expert Features:**
214
- - Analyzes entire conversation history
215
- - Performs web research when needed
216
- - Provides deep insights and recommendations
217
- - Acts as expert consultant in your conversation
218
- """)
219
-
220
- # Show conversation preview for HF expert
221
- st.markdown("**Conversation Preview for HF Expert:**")
222
- st.markdown("---")
223
- for i, msg in enumerate(st.session_state.messages[-5:]): # Last 5 messages
224
- role = "👤 You" if msg["role"] == "user" else "🤖 Assistant"
225
- st.markdown(f"**{role}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
226
- st.markdown("---")
227
-
228
- # Show web search determination
229
- try:
230
- user_session = session_manager.get_session("default_user")
231
- conversation_history = user_session.get("conversation", [])
232
- research_needs = coordinator.determine_web_search_needs(conversation_history)
233
-
234
- if research_needs["needs_search"]:
235
- st.info(f"🔍 **Research Needed:** {research_needs['reasoning']}")
236
- else:
237
- st.success("✅ No research needed for this conversation")
238
- except Exception as e:
239
- st.warning("⚠️ Could not determine research needs")
240
-
241
- with col2:
242
- if st.button("🧠 Activate HF Expert",
243
- key="activate_hf_expert",
244
- help="Send conversation to HF endpoint for deep analysis",
245
- use_container_width=True,
246
- disabled=st.session_state.is_processing):
247
- st.session_state.hf_expert_requested = True
248
-
249
- # Show HF expert analysis when requested (outside of the expander)
250
- if st.session_state.get("hf_expert_requested", False):
251
- with st.spinner("🧠 HF Expert analyzing conversation..."):
252
- try:
253
- # Get conversation history
254
- user_session = session_manager.get_session("default_user")
255
- conversation_history = user_session.get("conversation", [])
256
-
257
- # Show what HF expert sees in a separate expander
258
- with st.expander("📋 HF Expert Input", expanded=False):
259
- st.markdown("**Conversation History Sent to HF Expert:**")
260
- for i, msg in enumerate(conversation_history[-10:]): # Last 10 messages
261
- st.markdown(f"**{msg['role'].capitalize()}:** {msg['content'][:100]}{'...' if len(msg['content']) > 100 else ''}")
262
-
263
- # Request HF analysis
264
- hf_analysis = coordinator.manual_hf_analysis(
265
- "default_user",
266
- conversation_history
267
- )
268
-
269
- if hf_analysis:
270
- # Display HF expert response with clear indication
271
- with st.chat_message("assistant"):
272
- st.markdown("### 🤖 HF Expert Analysis")
273
- st.markdown(hf_analysis)
274
-
275
- # Add research/web search decisions
276
- research_needs = coordinator.determine_web_search_needs(conversation_history)
277
- if research_needs["needs_search"]:
278
- st.info(f"🔍 **Research Needed:** {research_needs['reasoning']}")
279
- if st.button("🔎 Perform Web Research", key="web_research_button"):
280
- # Perform web search
281
- with st.spinner("🔎 Searching for current information..."):
282
- # Add web search logic here
283
- st.success("✅ Web research completed!")
284
-
285
- # Add to message history with HF expert tag
286
- st.session_state.messages.append({
287
- "role": "assistant",
288
- "content": hf_analysis,
289
- "timestamp": datetime.now().strftime("%H:%M:%S"),
290
- "source": "hf_expert",
291
- "research_needs": research_needs
292
- })
293
-
294
- st.session_state.hf_expert_requested = False
295
-
296
- except Exception as e:
297
- user_msg = translate_error(e)
298
- st.error(f"❌ HF Expert analysis failed: {user_msg}")
299
- st.session_state.hf_expert_requested = False
300
-
301
  # Input validation function
302
  def validate_user_input(text):
303
  """Validate and sanitize user input"""
@@ -384,6 +303,11 @@ if user_input and not st.session_state.is_processing:
384
  # Stage 2: HF Endpoint Analysis
385
  status_placeholder.info("🛰️ Beaming Query to Orbital Station...")
386
  if config.hf_token:
 
 
 
 
 
387
  hf_response = send_to_hf(validated_input, conversation_history)
388
  if hf_response:
389
  with st.chat_message("assistant"):
@@ -462,6 +386,11 @@ if user_input and not st.session_state.is_processing:
462
  if config.hf_token and not ai_response:
463
  status_placeholder.info("⚡ Initializing HF Endpoint (2–4 minutes)...")
464
  try:
 
 
 
 
 
465
  ai_response = send_to_hf(validated_input, conversation_history)
466
  if ai_response:
467
  response_placeholder.markdown(ai_response)
@@ -511,11 +440,11 @@ if user_input and not st.session_state.is_processing:
511
  "content": f"⚠️ {user_msg}",
512
  "timestamp": datetime.now().strftime("%H:%M:%S")
513
  })
514
-
515
- # Moved finally block to proper location
516
- st.session_state.is_processing = False
517
- time.sleep(0.5) # Brief pause
518
- st.experimental_rerun()
519
 
520
  # Add evaluation dashboard tab (separate from chat interface)
521
  st.divider()
@@ -589,12 +518,14 @@ with tab2:
589
 
590
  with col2:
591
  try:
592
- from services.hf_endpoint_monitor import hf_monitor
593
  hf_status = hf_monitor.check_endpoint_status()
594
  if hf_status['available']:
595
- st.success("🤗 HF: Available")
 
 
 
596
  else:
597
- st.warning("🤗 HF: Not available")
598
  except:
599
  st.info("🤗 HF: Unknown")
600
 
@@ -651,6 +582,8 @@ with tab2:
651
  features.append("Web Search")
652
  if config.openweather_api_key:
653
  features.append("Weather Data")
 
 
654
 
655
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
656
 
@@ -695,19 +628,32 @@ with tab3:
695
  - **Persistent memory**: Uses Redis for conversation history storage
696
  - **Hierarchical reasoning**: Fast local responses with deep cloud analysis
697
 
698
- ### 🚀 Cosmic Cascade Mode
699
  When enabled, the AI follows a three-stage response pattern:
700
  1. **🐱 Cosmic Kitten Response**: Immediate local processing
701
  2. **🛰️ Orbital Station Analysis**: Deep cloud-based analysis
702
- 3. **🌟 Final Synthesis**: Unified response combining both perspectives
703
 
704
  ### 🛠️ Technical Architecture
705
  - **Primary model**: Ollama (local processing for fast responses)
706
  - **Secondary model**: Hugging Face Inference API (deep analysis)
707
- - **External data**: Web search and weather data
708
  - **Memory system**: Redis-based session management
709
 
710
  ### 📊 Evaluation Tools
711
  - Behavior testing with sample prompts
712
  - Performance metrics and analytics
713
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  from core.memory import check_redis_health
15
  from core.coordinator import coordinator
16
  from core.errors import translate_error
17
+ from core.personality import personality
18
+ from services.nasa_api import nasa_service
19
+ from services.hf_endpoint_monitor import hf_monitor
20
  import logging
21
 
22
  # Set up logging
 
34
  st.session_state.is_processing = False
35
  if "ngrok_url_temp" not in st.session_state:
36
  st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
 
 
37
  if "cosmic_mode" not in st.session_state:
38
  st.session_state.cosmic_mode = True # Default to cosmic mode
39
+ if "show_welcome" not in st.session_state:
40
+ st.session_state.show_welcome = True
41
+
42
+ # Fetch NASA data once per session
43
+ if "nasa_data" not in st.session_state:
44
+ st.session_state.nasa_data = {
45
+ "apod": nasa_service.get_apod() if config.nasa_api_key else None,
46
+ "space_weather": nasa_service.get_space_weather_alerts() if config.nasa_api_key else None,
47
+ "mars_weather": nasa_service.get_mars_weather() if config.nasa_api_key else None
48
+ }
49
 
50
  # Sidebar layout redesign
51
  with st.sidebar:
 
67
  )
68
  st.session_state.selected_model = model_options[selected_model_name]
69
 
70
+ # Toggle for cosmic mode using checkbox (since toggle doesn't exist in older versions)
71
+ st.session_state.cosmic_mode = st.checkbox("Enable Cosmic Mode", value=st.session_state.cosmic_mode)
72
 
73
  st.divider()
74
 
 
111
 
112
  st.divider()
113
 
114
+ # SYSTEM STATUS
115
+ with st.expander("🔍 System Status", expanded=False):
116
  st.subheader("📊 System Monitor")
117
  try:
118
  from services.ollama_monitor import check_ollama_status
 
125
  st.info("🦙 Ollama: Unknown")
126
 
127
  try:
 
128
  hf_status = hf_monitor.check_endpoint_status()
129
  if hf_status['available']:
130
+ if hf_status.get('initialized', False):
131
+ st.success("🤗 HF: Available & Initialized")
132
+ else:
133
+ st.warning("⚡ HF: Initializing...")
134
  else:
135
+ st.info("🤗 HF: Not configured")
136
  except:
137
  st.info("🤗 HF: Unknown")
138
 
 
143
 
144
  st.divider()
145
 
146
+ # NASA Context Display
147
+ if st.session_state.nasa_data.get("apod"):
148
+ apod = st.session_state.nasa_data["apod"]
149
+ st.subheader("🌌 Cosmic Context")
150
+ if apod.get("media_type") == "image" and apod.get("url"):
151
+ st.image(apod["url"], caption=apod.get("title", "Astronomy Picture of the Day"), width=200)
152
+ st.markdown(f"**{apod.get('title', 'Cosmic Phenomenon')}**")
153
+ st.caption(apod.get("explanation", "")[:100] + "..." if len(apod.get("explanation", "")) > 100 else apod.get("explanation", ""))
 
 
 
 
 
 
154
 
155
  st.divider()
156
  st.subheader("🐛 Debug Info")
157
  # Show current configuration
158
  st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
159
  st.markdown(f"**Model:** {st.session_state.selected_model}")
 
160
  st.markdown(f"**Cosmic Mode:** {'Enabled' if st.session_state.cosmic_mode else 'Disabled'}")
161
 
162
  # Show active features
 
167
  features.append("Web Search")
168
  if config.openweather_api_key:
169
  features.append("Weather")
170
+ if config.nasa_api_key:
171
+ features.append("Space Data")
172
 
173
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
174
 
 
176
  st.title("🐱 CosmicCat AI Assistant")
177
  st.markdown("Ask me anything about personal development, goal setting, or life advice!")
178
 
179
+ # Show welcome message only once
180
+ if st.session_state.show_welcome:
181
+ with st.chat_message("assistant"):
182
+ greeting = personality.get_greeting()
183
+ nasa_context = personality.get_nasa_context(st.session_state.nasa_data)
184
+ if nasa_context:
185
+ greeting += f"\n\n📍 {nasa_context}"
186
+ st.markdown(greeting)
187
+ st.session_state.show_welcome = False
188
+
189
  # Consistent message rendering function with cosmic styling
190
  def render_message(role, content, source=None, timestamp=None):
191
  """Render chat messages with consistent styling"""
 
199
  st.markdown(f"### 🌟 Final Cosmic Summary:")
200
  elif source == "error":
201
  st.markdown(f"### ❌ Error:")
202
+ elif source == "space_story":
203
+ st.markdown(f"### 🐱 Cosmic Kitten Story:")
204
  else:
205
  st.markdown(f"### {source}")
206
 
 
217
  message.get("timestamp")
218
  )
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  # Input validation function
221
  def validate_user_input(text):
222
  """Validate and sanitize user input"""
 
303
  # Stage 2: HF Endpoint Analysis
304
  status_placeholder.info("🛰️ Beaming Query to Orbital Station...")
305
  if config.hf_token:
306
+ # Check HF status first
307
+ hf_status = hf_monitor.check_endpoint_status()
308
+ if not hf_status['available']:
309
+ status_placeholder.info(personality.get_initializing_message())
310
+
311
  hf_response = send_to_hf(validated_input, conversation_history)
312
  if hf_response:
313
  with st.chat_message("assistant"):
 
386
  if config.hf_token and not ai_response:
387
  status_placeholder.info("⚡ Initializing HF Endpoint (2–4 minutes)...")
388
  try:
389
+ # Check HF status first
390
+ hf_status = hf_monitor.check_endpoint_status()
391
+ if not hf_status['available']:
392
+ status_placeholder.info(personality.get_initializing_message())
393
+
394
  ai_response = send_to_hf(validated_input, conversation_history)
395
  if ai_response:
396
  response_placeholder.markdown(ai_response)
 
440
  "content": f"⚠️ {user_msg}",
441
  "timestamp": datetime.now().strftime("%H:%M:%S")
442
  })
443
+
444
+ # Moved finally block to proper location
445
+ st.session_state.is_processing = False
446
+ time.sleep(0.5) # Brief pause
447
+ st.experimental_rerun()
448
 
449
  # Add evaluation dashboard tab (separate from chat interface)
450
  st.divider()
 
518
 
519
  with col2:
520
  try:
 
521
  hf_status = hf_monitor.check_endpoint_status()
522
  if hf_status['available']:
523
+ if hf_status.get('initialized', False):
524
+ st.success("🤗 HF: Available & Initialized")
525
+ else:
526
+ st.warning("⚡ HF: Initializing...")
527
  else:
528
+ st.info("🤗 HF: Not configured")
529
  except:
530
  st.info("🤗 HF: Unknown")
531
 
 
582
  features.append("Web Search")
583
  if config.openweather_api_key:
584
  features.append("Weather Data")
585
+ if config.nasa_api_key:
586
+ features.append("Space Data")
587
 
588
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
589
 
 
628
  - **Persistent memory**: Uses Redis for conversation history storage
629
  - **Hierarchical reasoning**: Fast local responses with deep cloud analysis
630
 
631
+ ### 🚀 Cosmic Mode
632
  When enabled, the AI follows a three-stage response pattern:
633
  1. **🐱 Cosmic Kitten Response**: Immediate local processing
634
  2. **🛰️ Orbital Station Analysis**: Deep cloud-based analysis
635
+ 3. **🌟 Final Synthesis**: Unified response combining both perspectives
636
 
637
  ### 🛠️ Technical Architecture
638
  - **Primary model**: Ollama (local processing for fast responses)
639
  - **Secondary model**: Hugging Face Inference API (deep analysis)
640
+ - **External data**: Web search, weather data, and space information
641
  - **Memory system**: Redis-based session management
642
 
643
  ### 📊 Evaluation Tools
644
  - Behavior testing with sample prompts
645
  - Performance metrics and analytics
646
  """)
647
+
648
+ # Add special command handling
649
+ if user_input and user_input.lower().strip() in ["hello", "hi", "hey"]:
650
+ with st.chat_message("assistant"):
651
+ story = personality.get_space_story()
652
+ st.markdown(f"### 🐱 Cosmic Kitten Story:\n\n{story}")
653
+
654
+ st.session_state.messages.append({
655
+ "role": "assistant",
656
+ "content": story,
657
+ "source": "space_story",
658
+ "timestamp": datetime.now().strftime("%H:%M:%S")
659
+ })
core/personality.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from datetime import datetime
3
+ from typing import Dict, Optional
4
+
5
+ class CosmicCatPersonality:
6
+ """Handles the personality and greetings for CosmicCat"""
7
+
8
+ def __init__(self):
9
+ self.space_greetings = [
10
+ "Purr... Welcome to the cosmos, traveler! 🐱✨",
11
+ "Meow! Ready to explore the universe of possibilities? 🌌",
12
+ "Greetings from the space station! Let's dive into cosmic wisdom! 🚀",
13
+ "Paws and reflect... What stellar insights shall we uncover today? ⭐",
14
+ "Telepathic meow! I sense you're seeking cosmic guidance! 🌠"
15
+ ]
16
+
17
+ self.space_stories = [
18
+ "In the depths of Andromeda, a cybernetic cat discovered the secret of quantum purring, bending reality with each harmonic vibration.",
19
+ "A space-faring kitten once navigated through a black hole and emerged in a parallel dimension where all cats rule supreme.",
20
+ "Legend tells of a cosmic cat whose whiskers could map the entire galaxy, guiding lost spacecraft back to their home worlds.",
21
+ "On the rings of Saturn, a wise feline meditated for eons, learning to communicate through the subtle frequencies of celestial bodies.",
22
+ "A brave kitten once rode a comet through the Milky Way, collecting stardust to weave dreams for sleeping planets."
23
+ ]
24
+
25
+ self.initializing_messages = [
26
+ "Charging my quantum paws... 🐾⚡",
27
+ "Aligning my cosmic whiskers... 🌌",
28
+ "Calibrating interstellar sensors... 🛰️",
29
+ "Warming up my thought engines... 🚀",
30
+ "Syncing with the galactic mainframe... 🌠",
31
+ "Boosting signal from deep space... 📡",
32
+ "Powering up my neural net... 💫"
33
+ ]
34
+
35
+ def get_greeting(self) -> str:
36
+ """Get a personalized space-themed greeting"""
37
+ hour = datetime.now().hour
38
+
39
+ if 5 <= hour < 12:
40
+ time_greeting = "Good morning, star voyager! ☀️"
41
+ elif 12 <= hour < 17:
42
+ time_greeting = "Good afternoon, cosmic explorer! 🌞"
43
+ elif 17 <= hour < 21:
44
+ time_greeting = "Good evening, space wanderer! 🌅"
45
+ else:
46
+ time_greeting = "Stellar night, dreamer! 🌙"
47
+
48
+ space_greeting = random.choice(self.space_greetings)
49
+ return f"{time_greeting}\n\n{space_greeting}"
50
+
51
+ def get_space_story(self) -> str:
52
+ """Get a random space cat story"""
53
+ return random.choice(self.space_stories)
54
+
55
+ def get_initializing_message(self) -> str:
56
+ """Get a random initialization message"""
57
+ return random.choice(self.initializing_messages)
58
+
59
+ def get_nasa_context(self, nasa_data: Optional[Dict]) -> str:
60
+ """Create context based on NASA data"""
61
+ if not nasa_data:
62
+ return ""
63
+
64
+ context_parts = []
65
+
66
+ # Add APOD context
67
+ if 'apod' in nasa_data and nasa_data['apod']:
68
+ apod = nasa_data['apod']
69
+ context_parts.append(f"🌌 Today's cosmic view: {apod.get('title', 'Unknown phenomenon')}")
70
+
71
+ # Add space weather context
72
+ if 'space_weather' in nasa_data and nasa_data['space_weather']:
73
+ weather = nasa_data['space_weather']
74
+ context_parts.append("🛰️ Space weather is stable for cosmic communications")
75
+
76
+ # Add Mars weather if available
77
+ if 'mars_weather' in nasa_data and nasa_data['mars_weather']:
78
+ mars = nasa_data['mars_weather']
79
+ context_parts.append("🪐 Martian conditions are optimal for interplanetary contemplation")
80
+
81
+ return " | ".join(context_parts) if context_parts else ""
82
+
83
+ # Global instance
84
+ personality = CosmicCatPersonality()
core/providers/ollama.py CHANGED
@@ -5,21 +5,22 @@ from datetime import datetime
5
  from typing import List, Dict, Optional, Union
6
  from core.providers.base import LLMProvider
7
  from utils.config import config
 
8
 
9
  logger = logging.getLogger(__name__)
10
 
11
  class OllamaProvider(LLMProvider):
12
  """Ollama LLM provider implementation"""
13
-
14
  def __init__(self, model_name: str, timeout: int = 60, max_retries: int = 3): # Increased timeout from 30 to 60
15
  super().__init__(model_name, timeout, max_retries)
16
  self.host = self._sanitize_host(config.ollama_host or "http://localhost:11434")
17
  # Headers to skip ngrok browser warning
18
  self.headers = {
19
  "ngrok-skip-browser-warning": "true",
20
- "User-Agent": "AI-Life-Coach-Ollama"
21
  }
22
-
23
  def _sanitize_host(self, host: str) -> str:
24
  """Sanitize host URL by removing whitespace and control characters"""
25
  if not host:
@@ -32,7 +33,7 @@ class OllamaProvider(LLMProvider):
32
  if not host.startswith(('http://', 'https://')):
33
  host = 'http://' + host
34
  return host
35
-
36
  def generate(self, prompt: str, conversation_history: List[Dict]) -> Optional[str]:
37
  """Generate a response synchronously"""
38
  try:
@@ -40,7 +41,7 @@ class OllamaProvider(LLMProvider):
40
  except Exception as e:
41
  logger.error(f"Ollama generation failed: {e}")
42
  return None
43
-
44
  def stream_generate(self, prompt: str, conversation_history: List[Dict]) -> Optional[Union[str, List[str]]]:
45
  """Generate a response with streaming support"""
46
  try:
@@ -48,7 +49,7 @@ class OllamaProvider(LLMProvider):
48
  except Exception as e:
49
  logger.error(f"Ollama stream generation failed: {e}")
50
  return None
51
-
52
  def validate_model(self) -> bool:
53
  """Validate if the model is available"""
54
  try:
@@ -73,25 +74,22 @@ class OllamaProvider(LLMProvider):
73
  except Exception as e:
74
  logger.error(f"Model validation failed: {e}")
75
  return False
76
-
77
  def _generate_impl(self, prompt: str, conversation_history: List[Dict]) -> str:
78
  """Implementation of synchronous generation"""
79
  url = f"{self.host}/api/chat"
80
  messages = conversation_history.copy()
81
 
82
- # Inject current time as first message
83
- current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
84
- time_context = {"role": "system", "content": f"[Current Date & Time: {current_time}]"}
85
- messages = [time_context] + messages
86
-
87
  # Add the current prompt if not already in history
88
  if not messages or messages[-1].get("content") != prompt:
89
  messages.append({"role": "user", "content": prompt})
 
90
  payload = {
91
  "model": self.model_name,
92
  "messages": messages,
93
  "stream": False
94
  }
 
95
  response = requests.post(
96
  url,
97
  json=payload,
@@ -101,25 +99,22 @@ class OllamaProvider(LLMProvider):
101
  response.raise_for_status()
102
  result = response.json()
103
  return result["message"]["content"]
104
-
105
  def _stream_generate_impl(self, prompt: str, conversation_history: List[Dict]) -> List[str]:
106
  """Implementation of streaming generation"""
107
  url = f"{self.host}/api/chat"
108
  messages = conversation_history.copy()
109
 
110
- # Inject current time as first message
111
- current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
112
- time_context = {"role": "system", "content": f"[Current Date & Time: {current_time}]"}
113
- messages = [time_context] + messages
114
-
115
  # Add the current prompt if not already in history
116
  if not messages or messages[-1].get("content") != prompt:
117
  messages.append({"role": "user", "content": prompt})
 
118
  payload = {
119
  "model": self.model_name,
120
  "messages": messages,
121
  "stream": True
122
  }
 
123
  response = requests.post(
124
  url,
125
  json=payload,
 
5
  from typing import List, Dict, Optional, Union
6
  from core.providers.base import LLMProvider
7
  from utils.config import config
8
+ from core.personality import personality
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
  class OllamaProvider(LLMProvider):
13
  """Ollama LLM provider implementation"""
14
+
15
  def __init__(self, model_name: str, timeout: int = 60, max_retries: int = 3): # Increased timeout from 30 to 60
16
  super().__init__(model_name, timeout, max_retries)
17
  self.host = self._sanitize_host(config.ollama_host or "http://localhost:11434")
18
  # Headers to skip ngrok browser warning
19
  self.headers = {
20
  "ngrok-skip-browser-warning": "true",
21
+ "User-Agent": "CosmicCat-AI-Assistant"
22
  }
23
+
24
  def _sanitize_host(self, host: str) -> str:
25
  """Sanitize host URL by removing whitespace and control characters"""
26
  if not host:
 
33
  if not host.startswith(('http://', 'https://')):
34
  host = 'http://' + host
35
  return host
36
+
37
  def generate(self, prompt: str, conversation_history: List[Dict]) -> Optional[str]:
38
  """Generate a response synchronously"""
39
  try:
 
41
  except Exception as e:
42
  logger.error(f"Ollama generation failed: {e}")
43
  return None
44
+
45
  def stream_generate(self, prompt: str, conversation_history: List[Dict]) -> Optional[Union[str, List[str]]]:
46
  """Generate a response with streaming support"""
47
  try:
 
49
  except Exception as e:
50
  logger.error(f"Ollama stream generation failed: {e}")
51
  return None
52
+
53
  def validate_model(self) -> bool:
54
  """Validate if the model is available"""
55
  try:
 
74
  except Exception as e:
75
  logger.error(f"Model validation failed: {e}")
76
  return False
77
+
78
  def _generate_impl(self, prompt: str, conversation_history: List[Dict]) -> str:
79
  """Implementation of synchronous generation"""
80
  url = f"{self.host}/api/chat"
81
  messages = conversation_history.copy()
82
 
 
 
 
 
 
83
  # Add the current prompt if not already in history
84
  if not messages or messages[-1].get("content") != prompt:
85
  messages.append({"role": "user", "content": prompt})
86
+
87
  payload = {
88
  "model": self.model_name,
89
  "messages": messages,
90
  "stream": False
91
  }
92
+
93
  response = requests.post(
94
  url,
95
  json=payload,
 
99
  response.raise_for_status()
100
  result = response.json()
101
  return result["message"]["content"]
102
+
103
  def _stream_generate_impl(self, prompt: str, conversation_history: List[Dict]) -> List[str]:
104
  """Implementation of streaming generation"""
105
  url = f"{self.host}/api/chat"
106
  messages = conversation_history.copy()
107
 
 
 
 
 
 
108
  # Add the current prompt if not already in history
109
  if not messages or messages[-1].get("content") != prompt:
110
  messages.append({"role": "user", "content": prompt})
111
+
112
  payload = {
113
  "model": self.model_name,
114
  "messages": messages,
115
  "stream": True
116
  }
117
+
118
  response = requests.post(
119
  url,
120
  json=payload,
services/nasa_api.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+ import random
4
+ from typing import Optional, Dict
5
+ from utils.config import config
6
+
7
+ class NasaService:
8
+ """Service for fetching NASA data including space weather"""
9
+
10
+ def __init__(self):
11
+ self.api_key = config.nasa_api_key or os.getenv("NASA_API_KEY")
12
+ self.base_url = "https://api.nasa.gov"
13
+
14
+ def get_apod(self) -> Optional[Dict]:
15
+ """Get Astronomy Picture of the Day"""
16
+ if not self.api_key:
17
+ return None
18
+
19
+ try:
20
+ params = {
21
+ 'api_key': self.api_key,
22
+ 'thumbs': 'true'
23
+ }
24
+
25
+ response = requests.get(
26
+ f"{self.base_url}/planetary/apod",
27
+ params=params,
28
+ timeout=10
29
+ )
30
+
31
+ if response.status_code == 200:
32
+ return response.json()
33
+ return None
34
+ except Exception as e:
35
+ print(f"Error fetching APOD: {e}")
36
+ return None
37
+
38
+ def get_space_weather_alerts(self) -> Optional[Dict]:
39
+ """Get space weather alerts"""
40
+ try:
41
+ response = requests.get(
42
+ f"{self.base_url}/DONKI/notifications",
43
+ params={'api_key': self.api_key or 'DEMO_KEY'},
44
+ timeout=10
45
+ )
46
+
47
+ if response.status_code == 200:
48
+ data = response.json()
49
+ # Get recent alerts (last 7 days)
50
+ if isinstance(data, list) and len(data) > 0:
51
+ return data[0] # Most recent alert
52
+ return None
53
+ except Exception as e:
54
+ print(f"Error fetching space weather: {e}")
55
+ return None
56
+
57
+ def get_mars_weather(self) -> Optional[Dict]:
58
+ """Get Mars weather data"""
59
+ try:
60
+ response = requests.get(
61
+ f"{self.base_url}/insight_weather/",
62
+ params={
63
+ 'api_key': self.api_key or 'DEMO_KEY',
64
+ 'feedtype': 'json',
65
+ 'ver': '1.0'
66
+ },
67
+ timeout=10
68
+ )
69
+
70
+ if response.status_code == 200:
71
+ return response.json()
72
+ return None
73
+ except Exception as e:
74
+ print(f"Error fetching Mars weather: {e}")
75
+ return None
76
+
77
+ # Global instance
78
+ nasa_service = NasaService()