Implement Cosmic Cascade Response Mode with three-stage AI flow
Browse files- app.py +104 -116
- core/coordinator.py +154 -155
- core/personality.py +9 -9
app.py
CHANGED
|
@@ -7,7 +7,6 @@ import asyncio
|
|
| 7 |
from datetime import datetime
|
| 8 |
from pathlib import Path
|
| 9 |
sys.path.append(str(Path(__file__).parent))
|
| 10 |
-
|
| 11 |
from utils.config import config
|
| 12 |
from core.llm import send_to_ollama, send_to_hf
|
| 13 |
from core.session import session_manager
|
|
@@ -67,7 +66,7 @@ with st.sidebar:
|
|
| 67 |
)
|
| 68 |
st.session_state.selected_model = model_options[selected_model_name]
|
| 69 |
|
| 70 |
-
# Toggle for cosmic mode using checkbox
|
| 71 |
st.session_state.cosmic_mode = st.checkbox("Enable Cosmic Mode", value=st.session_state.cosmic_mode)
|
| 72 |
|
| 73 |
st.divider()
|
|
@@ -84,7 +83,7 @@ with st.sidebar:
|
|
| 84 |
if ngrok_url_input != st.session_state.ngrok_url_temp:
|
| 85 |
st.session_state.ngrok_url_temp = ngrok_url_input
|
| 86 |
st.success("β
URL updated!")
|
| 87 |
-
|
| 88 |
if st.button("π‘ Test Connection"):
|
| 89 |
try:
|
| 90 |
import requests
|
|
@@ -104,11 +103,11 @@ with st.sidebar:
|
|
| 104 |
st.error(f"β Failed: {response.status_code}")
|
| 105 |
except Exception as e:
|
| 106 |
st.error(f"β Error: {str(e)[:50]}...")
|
| 107 |
-
|
| 108 |
if st.button("ποΈ Clear History"):
|
| 109 |
st.session_state.messages = []
|
| 110 |
st.success("History cleared!")
|
| 111 |
-
|
| 112 |
st.divider()
|
| 113 |
|
| 114 |
# SYSTEM STATUS
|
|
@@ -123,7 +122,7 @@ with st.sidebar:
|
|
| 123 |
st.warning("π¦ Ollama: Not running")
|
| 124 |
except:
|
| 125 |
st.info("π¦ Ollama: Unknown")
|
| 126 |
-
|
| 127 |
try:
|
| 128 |
hf_status = hf_monitor.check_endpoint_status()
|
| 129 |
if hf_status['available']:
|
|
@@ -135,42 +134,42 @@ with st.sidebar:
|
|
| 135 |
st.info("π€ HF: Not configured")
|
| 136 |
except:
|
| 137 |
st.info("π€ HF: Unknown")
|
| 138 |
-
|
| 139 |
if check_redis_health():
|
| 140 |
st.success("πΎ Redis: Connected")
|
| 141 |
else:
|
| 142 |
st.error("πΎ Redis: Disconnected")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
features = []
|
| 164 |
-
if config.hf_token:
|
| 165 |
-
features.append("HF Expert")
|
| 166 |
-
if os.getenv("TAVILY_API_KEY"):
|
| 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 |
|
| 175 |
# Main interface
|
| 176 |
st.title("π± CosmicCat AI Assistant")
|
|
@@ -203,7 +202,6 @@ def render_message(role, content, source=None, timestamp=None):
|
|
| 203 |
st.markdown(f"### π± Cosmic Kitten Story:")
|
| 204 |
else:
|
| 205 |
st.markdown(f"### {source}")
|
| 206 |
-
|
| 207 |
st.markdown(content)
|
| 208 |
if timestamp:
|
| 209 |
st.caption(f"π {timestamp}")
|
|
@@ -211,9 +209,9 @@ def render_message(role, content, source=None, timestamp=None):
|
|
| 211 |
# Display messages
|
| 212 |
for message in st.session_state.messages:
|
| 213 |
render_message(
|
| 214 |
-
message["role"],
|
| 215 |
-
message["content"],
|
| 216 |
-
message.get("source"),
|
| 217 |
message.get("timestamp")
|
| 218 |
)
|
| 219 |
|
|
@@ -222,7 +220,6 @@ def validate_user_input(text):
|
|
| 222 |
"""Validate and sanitize user input"""
|
| 223 |
if not text or not text.strip():
|
| 224 |
return False, "Input cannot be empty"
|
| 225 |
-
|
| 226 |
if len(text) > 1000:
|
| 227 |
return False, "Input too long (max 1000 characters)"
|
| 228 |
|
|
@@ -249,7 +246,7 @@ if user_input and not st.session_state.is_processing:
|
|
| 249 |
# Display user message
|
| 250 |
with st.chat_message("user"):
|
| 251 |
st.markdown(validated_input)
|
| 252 |
-
|
| 253 |
# Add to message history - ensure proper format
|
| 254 |
st.session_state.messages.append({
|
| 255 |
"role": "user",
|
|
@@ -283,7 +280,7 @@ if user_input and not st.session_state.is_processing:
|
|
| 283 |
# Stage 1: Local Ollama Response
|
| 284 |
status_placeholder.info("π± Cosmic Kitten Responding...")
|
| 285 |
local_response = send_to_ollama(
|
| 286 |
-
validated_input,
|
| 287 |
conversation_history,
|
| 288 |
st.session_state.ngrok_url_temp,
|
| 289 |
st.session_state.selected_model
|
|
@@ -292,14 +289,13 @@ if user_input and not st.session_state.is_processing:
|
|
| 292 |
if local_response:
|
| 293 |
with st.chat_message("assistant"):
|
| 294 |
st.markdown(f"### π± Cosmic Kitten Says:\n{local_response}")
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
# Stage 2: HF Endpoint Analysis
|
| 304 |
status_placeholder.info("π°οΈ Beaming Query to Orbital Station...")
|
| 305 |
if config.hf_token:
|
|
@@ -307,21 +303,22 @@ if user_input and not st.session_state.is_processing:
|
|
| 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"):
|
| 314 |
st.markdown(f"### π°οΈ Orbital Station Reports:\n{hf_response}")
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
# Stage 3: Local Synthesis
|
| 324 |
status_placeholder.info("π± Cosmic Kitten Synthesizing Wisdom...")
|
|
|
|
| 325 |
# Update history with both responses
|
| 326 |
synthesis_history = conversation_history.copy()
|
| 327 |
synthesis_history.extend([
|
|
@@ -339,16 +336,15 @@ if user_input and not st.session_state.is_processing:
|
|
| 339 |
if synthesis:
|
| 340 |
with st.chat_message("assistant"):
|
| 341 |
st.markdown(f"### π Final Cosmic Summary:\n{synthesis}")
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
status_placeholder.success("β¨ Cosmic Cascade Complete!")
|
| 351 |
-
|
| 352 |
except Exception as e:
|
| 353 |
error_msg = f"π Cosmic disturbance: {str(e)}"
|
| 354 |
st.error(error_msg)
|
|
@@ -377,30 +373,33 @@ if user_input and not st.session_state.is_processing:
|
|
| 377 |
status_placeholder.success("β
Response received!")
|
| 378 |
else:
|
| 379 |
status_placeholder.warning("β οΈ Empty response from Ollama")
|
| 380 |
-
|
| 381 |
except Exception as ollama_error:
|
| 382 |
user_msg = translate_error(ollama_error)
|
| 383 |
status_placeholder.error(f"β οΈ {user_msg}")
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 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 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
# Save response if successful
|
| 405 |
if ai_response:
|
| 406 |
# Update conversation history
|
|
@@ -431,7 +430,7 @@ if user_input and not st.session_state.is_processing:
|
|
| 431 |
"content": "Sorry, I couldn't process your request. Please try again.",
|
| 432 |
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 433 |
})
|
| 434 |
-
|
| 435 |
except Exception as e:
|
| 436 |
user_msg = translate_error(e)
|
| 437 |
response_placeholder.error(f"β οΈ {user_msg}")
|
|
@@ -440,7 +439,7 @@ if user_input and not st.session_state.is_processing:
|
|
| 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
|
|
@@ -464,7 +463,6 @@ with tab1:
|
|
| 464 |
|
| 465 |
selected_prompt = st.selectbox("Choose a test prompt:", eval_prompts)
|
| 466 |
custom_prompt = st.text_input("Or enter your own:", "")
|
| 467 |
-
|
| 468 |
final_prompt = custom_prompt or selected_prompt
|
| 469 |
|
| 470 |
if st.button("Evaluate"):
|
|
@@ -479,11 +477,9 @@ with tab1:
|
|
| 479 |
try:
|
| 480 |
ai_response = send_to_ollama(final_prompt, history, st.session_state.ngrok_url_temp, st.session_state.selected_model)
|
| 481 |
duration = round(time.time() - start_time, 2)
|
| 482 |
-
|
| 483 |
st.success(f"β
Response generated in {duration}s")
|
| 484 |
st.markdown("**Response:**")
|
| 485 |
st.write(ai_response)
|
| 486 |
-
|
| 487 |
st.markdown("**Analysis Tags:**")
|
| 488 |
tags = []
|
| 489 |
if "today" in final_prompt.lower() or "date" in final_prompt.lower():
|
|
@@ -493,7 +489,6 @@ with tab1:
|
|
| 493 |
if any(word in final_prompt.lower() for word in ["vitamin", "drug", "metformin", "CRISPR"]):
|
| 494 |
tags.append("𧬠Scientific Knowledge")
|
| 495 |
st.write(", ".join(tags) if tags else "General Knowledge")
|
| 496 |
-
|
| 497 |
except Exception as e:
|
| 498 |
st.error(f"Evaluation failed: {translate_error(e)}")
|
| 499 |
|
|
@@ -504,7 +499,6 @@ with tab2:
|
|
| 504 |
# System status
|
| 505 |
st.subheader("System Status")
|
| 506 |
col1, col2, col3 = st.columns(3)
|
| 507 |
-
|
| 508 |
with col1:
|
| 509 |
try:
|
| 510 |
from services.ollama_monitor import check_ollama_status
|
|
@@ -515,7 +509,6 @@ with tab2:
|
|
| 515 |
st.warning("π¦ Ollama: Not running")
|
| 516 |
except:
|
| 517 |
st.info("π¦ Ollama: Unknown")
|
| 518 |
-
|
| 519 |
with col2:
|
| 520 |
try:
|
| 521 |
hf_status = hf_monitor.check_endpoint_status()
|
|
@@ -528,13 +521,12 @@ with tab2:
|
|
| 528 |
st.info("π€ HF: Not configured")
|
| 529 |
except:
|
| 530 |
st.info("π€ HF: Unknown")
|
| 531 |
-
|
| 532 |
with col3:
|
| 533 |
if check_redis_health():
|
| 534 |
st.success("πΎ Redis: Connected")
|
| 535 |
else:
|
| 536 |
st.error("πΎ Redis: Disconnected")
|
| 537 |
-
|
| 538 |
# Session statistics
|
| 539 |
st.subheader("Session Statistics")
|
| 540 |
try:
|
|
@@ -551,7 +543,7 @@ with tab2:
|
|
| 551 |
st.info("No coordination statistics available yet.")
|
| 552 |
except Exception as e:
|
| 553 |
st.warning(f"Could not load session statistics: {translate_error(e)}")
|
| 554 |
-
|
| 555 |
# Recent activity
|
| 556 |
st.subheader("Recent Activity")
|
| 557 |
try:
|
|
@@ -565,7 +557,7 @@ with tab2:
|
|
| 565 |
st.info("No recent activity recorded.")
|
| 566 |
except Exception as e:
|
| 567 |
st.warning(f"Could not load recent activity: {translate_error(e)}")
|
| 568 |
-
|
| 569 |
# Configuration summary
|
| 570 |
st.subheader("Configuration Summary")
|
| 571 |
st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
|
|
@@ -584,7 +576,6 @@ with tab2:
|
|
| 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 |
|
| 590 |
# Conversation Analytics
|
|
@@ -592,7 +583,6 @@ with tab2:
|
|
| 592 |
try:
|
| 593 |
user_session = session_manager.get_session("default_user")
|
| 594 |
conversation = user_session.get("conversation", [])
|
| 595 |
-
|
| 596 |
if conversation:
|
| 597 |
# Analyze conversation patterns
|
| 598 |
user_messages = [msg for msg in conversation if msg["role"] == "user"]
|
|
@@ -601,9 +591,9 @@ with tab2:
|
|
| 601 |
col1, col2, col3 = st.columns(3)
|
| 602 |
col1.metric("Total Exchanges", len(user_messages))
|
| 603 |
col2.metric("Avg Response Length",
|
| 604 |
-
|
| 605 |
-
col3.metric("Topics Discussed",
|
| 606 |
-
|
| 607 |
|
| 608 |
# Show most common words/topics
|
| 609 |
all_text = " ".join([msg.get("content", "") for msg in conversation]).lower()
|
|
@@ -613,7 +603,6 @@ with tab2:
|
|
| 613 |
st.markdown(f"**Detected Topics:** {', '.join(relevant_topics)}")
|
| 614 |
else:
|
| 615 |
st.info("No conversation data available yet.")
|
| 616 |
-
|
| 617 |
except Exception as e:
|
| 618 |
st.warning(f"Could not analyze conversation: {translate_error(e)}")
|
| 619 |
|
|
@@ -632,7 +621,7 @@ with tab3:
|
|
| 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 |
-
|
| 636 |
|
| 637 |
### π οΈ Technical Architecture
|
| 638 |
- **Primary model**: Ollama (local processing for fast responses)
|
|
@@ -650,10 +639,9 @@ 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 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
})
|
|
|
|
| 7 |
from datetime import datetime
|
| 8 |
from pathlib import Path
|
| 9 |
sys.path.append(str(Path(__file__).parent))
|
|
|
|
| 10 |
from utils.config import config
|
| 11 |
from core.llm import send_to_ollama, send_to_hf
|
| 12 |
from core.session import session_manager
|
|
|
|
| 66 |
)
|
| 67 |
st.session_state.selected_model = model_options[selected_model_name]
|
| 68 |
|
| 69 |
+
# Toggle for cosmic mode using checkbox
|
| 70 |
st.session_state.cosmic_mode = st.checkbox("Enable Cosmic Mode", value=st.session_state.cosmic_mode)
|
| 71 |
|
| 72 |
st.divider()
|
|
|
|
| 83 |
if ngrok_url_input != st.session_state.ngrok_url_temp:
|
| 84 |
st.session_state.ngrok_url_temp = ngrok_url_input
|
| 85 |
st.success("β
URL updated!")
|
| 86 |
+
|
| 87 |
if st.button("π‘ Test Connection"):
|
| 88 |
try:
|
| 89 |
import requests
|
|
|
|
| 103 |
st.error(f"β Failed: {response.status_code}")
|
| 104 |
except Exception as e:
|
| 105 |
st.error(f"β Error: {str(e)[:50]}...")
|
| 106 |
+
|
| 107 |
if st.button("ποΈ Clear History"):
|
| 108 |
st.session_state.messages = []
|
| 109 |
st.success("History cleared!")
|
| 110 |
+
|
| 111 |
st.divider()
|
| 112 |
|
| 113 |
# SYSTEM STATUS
|
|
|
|
| 122 |
st.warning("π¦ Ollama: Not running")
|
| 123 |
except:
|
| 124 |
st.info("π¦ Ollama: Unknown")
|
| 125 |
+
|
| 126 |
try:
|
| 127 |
hf_status = hf_monitor.check_endpoint_status()
|
| 128 |
if hf_status['available']:
|
|
|
|
| 134 |
st.info("π€ HF: Not configured")
|
| 135 |
except:
|
| 136 |
st.info("π€ HF: Unknown")
|
| 137 |
+
|
| 138 |
if check_redis_health():
|
| 139 |
st.success("πΎ Redis: Connected")
|
| 140 |
else:
|
| 141 |
st.error("πΎ Redis: Disconnected")
|
| 142 |
+
|
| 143 |
+
st.divider()
|
| 144 |
+
|
| 145 |
+
# NASA Context Display
|
| 146 |
+
if st.session_state.nasa_data.get("apod"):
|
| 147 |
+
apod = st.session_state.nasa_data["apod"]
|
| 148 |
+
st.subheader("π Cosmic Context")
|
| 149 |
+
if apod.get("media_type") == "image" and apod.get("url"):
|
| 150 |
+
st.image(apod["url"], caption=apod.get("title", "Astronomy Picture of the Day"), width=200)
|
| 151 |
+
st.markdown(f"{apod.get('title', 'Cosmic Phenomenon')}")
|
| 152 |
+
st.caption(apod.get("explanation", "")[:100] + "..." if len(apod.get("explanation", "")) > 100 else apod.get("explanation", ""))
|
| 153 |
|
| 154 |
+
st.divider()
|
| 155 |
+
|
| 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
|
| 163 |
+
features = []
|
| 164 |
+
if config.hf_token:
|
| 165 |
+
features.append("HF Expert")
|
| 166 |
+
if os.getenv("TAVILY_API_KEY"):
|
| 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 |
+
st.markdown(f"Active Features: {', '.join(features) if features else 'None'}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
# Main interface
|
| 175 |
st.title("π± CosmicCat AI Assistant")
|
|
|
|
| 202 |
st.markdown(f"### π± Cosmic Kitten Story:")
|
| 203 |
else:
|
| 204 |
st.markdown(f"### {source}")
|
|
|
|
| 205 |
st.markdown(content)
|
| 206 |
if timestamp:
|
| 207 |
st.caption(f"π {timestamp}")
|
|
|
|
| 209 |
# Display messages
|
| 210 |
for message in st.session_state.messages:
|
| 211 |
render_message(
|
| 212 |
+
message["role"],
|
| 213 |
+
message["content"],
|
| 214 |
+
message.get("source"),
|
| 215 |
message.get("timestamp")
|
| 216 |
)
|
| 217 |
|
|
|
|
| 220 |
"""Validate and sanitize user input"""
|
| 221 |
if not text or not text.strip():
|
| 222 |
return False, "Input cannot be empty"
|
|
|
|
| 223 |
if len(text) > 1000:
|
| 224 |
return False, "Input too long (max 1000 characters)"
|
| 225 |
|
|
|
|
| 246 |
# Display user message
|
| 247 |
with st.chat_message("user"):
|
| 248 |
st.markdown(validated_input)
|
| 249 |
+
|
| 250 |
# Add to message history - ensure proper format
|
| 251 |
st.session_state.messages.append({
|
| 252 |
"role": "user",
|
|
|
|
| 280 |
# Stage 1: Local Ollama Response
|
| 281 |
status_placeholder.info("π± Cosmic Kitten Responding...")
|
| 282 |
local_response = send_to_ollama(
|
| 283 |
+
validated_input,
|
| 284 |
conversation_history,
|
| 285 |
st.session_state.ngrok_url_temp,
|
| 286 |
st.session_state.selected_model
|
|
|
|
| 289 |
if local_response:
|
| 290 |
with st.chat_message("assistant"):
|
| 291 |
st.markdown(f"### π± Cosmic Kitten Says:\n{local_response}")
|
| 292 |
+
st.session_state.messages.append({
|
| 293 |
+
"role": "assistant",
|
| 294 |
+
"content": local_response,
|
| 295 |
+
"source": "local_kitty",
|
| 296 |
+
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 297 |
+
})
|
| 298 |
+
|
|
|
|
| 299 |
# Stage 2: HF Endpoint Analysis
|
| 300 |
status_placeholder.info("π°οΈ Beaming Query to Orbital Station...")
|
| 301 |
if config.hf_token:
|
|
|
|
| 303 |
hf_status = hf_monitor.check_endpoint_status()
|
| 304 |
if not hf_status['available']:
|
| 305 |
status_placeholder.info(personality.get_initializing_message())
|
| 306 |
+
|
| 307 |
hf_response = send_to_hf(validated_input, conversation_history)
|
| 308 |
+
|
| 309 |
if hf_response:
|
| 310 |
with st.chat_message("assistant"):
|
| 311 |
st.markdown(f"### π°οΈ Orbital Station Reports:\n{hf_response}")
|
| 312 |
+
st.session_state.messages.append({
|
| 313 |
+
"role": "assistant",
|
| 314 |
+
"content": hf_response,
|
| 315 |
+
"source": "orbital_station",
|
| 316 |
+
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 317 |
+
})
|
| 318 |
+
|
|
|
|
| 319 |
# Stage 3: Local Synthesis
|
| 320 |
status_placeholder.info("π± Cosmic Kitten Synthesizing Wisdom...")
|
| 321 |
+
|
| 322 |
# Update history with both responses
|
| 323 |
synthesis_history = conversation_history.copy()
|
| 324 |
synthesis_history.extend([
|
|
|
|
| 336 |
if synthesis:
|
| 337 |
with st.chat_message("assistant"):
|
| 338 |
st.markdown(f"### π Final Cosmic Summary:\n{synthesis}")
|
| 339 |
+
st.session_state.messages.append({
|
| 340 |
+
"role": "assistant",
|
| 341 |
+
"content": synthesis,
|
| 342 |
+
"source": "cosmic_summary",
|
| 343 |
+
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 344 |
+
})
|
| 345 |
+
|
|
|
|
| 346 |
status_placeholder.success("β¨ Cosmic Cascade Complete!")
|
| 347 |
+
|
| 348 |
except Exception as e:
|
| 349 |
error_msg = f"π Cosmic disturbance: {str(e)}"
|
| 350 |
st.error(error_msg)
|
|
|
|
| 373 |
status_placeholder.success("β
Response received!")
|
| 374 |
else:
|
| 375 |
status_placeholder.warning("β οΈ Empty response from Ollama")
|
| 376 |
+
|
| 377 |
except Exception as ollama_error:
|
| 378 |
user_msg = translate_error(ollama_error)
|
| 379 |
status_placeholder.error(f"β οΈ {user_msg}")
|
| 380 |
+
|
| 381 |
+
# Fallback to HF if available
|
| 382 |
+
if config.hf_token and not ai_response:
|
| 383 |
+
status_placeholder.info("β‘ Initializing HF Endpoint (2β4 minutes)...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
+
try:
|
| 386 |
+
# Check HF status first
|
| 387 |
+
hf_status = hf_monitor.check_endpoint_status()
|
| 388 |
+
if not hf_status['available']:
|
| 389 |
+
status_placeholder.info(personality.get_initializing_message())
|
| 390 |
+
|
| 391 |
+
ai_response = send_to_hf(validated_input, conversation_history)
|
| 392 |
+
|
| 393 |
+
if ai_response:
|
| 394 |
+
response_placeholder.markdown(ai_response)
|
| 395 |
+
status_placeholder.success("β
HF response received!")
|
| 396 |
+
else:
|
| 397 |
+
status_placeholder.error("β No response from HF")
|
| 398 |
+
|
| 399 |
+
except Exception as hf_error:
|
| 400 |
+
user_msg = translate_error(hf_error)
|
| 401 |
+
status_placeholder.error(f"β οΈ {user_msg}")
|
| 402 |
+
|
| 403 |
# Save response if successful
|
| 404 |
if ai_response:
|
| 405 |
# Update conversation history
|
|
|
|
| 430 |
"content": "Sorry, I couldn't process your request. Please try again.",
|
| 431 |
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 432 |
})
|
| 433 |
+
|
| 434 |
except Exception as e:
|
| 435 |
user_msg = translate_error(e)
|
| 436 |
response_placeholder.error(f"β οΈ {user_msg}")
|
|
|
|
| 439 |
"content": f"β οΈ {user_msg}",
|
| 440 |
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 441 |
})
|
| 442 |
+
|
| 443 |
# Moved finally block to proper location
|
| 444 |
st.session_state.is_processing = False
|
| 445 |
time.sleep(0.5) # Brief pause
|
|
|
|
| 463 |
|
| 464 |
selected_prompt = st.selectbox("Choose a test prompt:", eval_prompts)
|
| 465 |
custom_prompt = st.text_input("Or enter your own:", "")
|
|
|
|
| 466 |
final_prompt = custom_prompt or selected_prompt
|
| 467 |
|
| 468 |
if st.button("Evaluate"):
|
|
|
|
| 477 |
try:
|
| 478 |
ai_response = send_to_ollama(final_prompt, history, st.session_state.ngrok_url_temp, st.session_state.selected_model)
|
| 479 |
duration = round(time.time() - start_time, 2)
|
|
|
|
| 480 |
st.success(f"β
Response generated in {duration}s")
|
| 481 |
st.markdown("**Response:**")
|
| 482 |
st.write(ai_response)
|
|
|
|
| 483 |
st.markdown("**Analysis Tags:**")
|
| 484 |
tags = []
|
| 485 |
if "today" in final_prompt.lower() or "date" in final_prompt.lower():
|
|
|
|
| 489 |
if any(word in final_prompt.lower() for word in ["vitamin", "drug", "metformin", "CRISPR"]):
|
| 490 |
tags.append("𧬠Scientific Knowledge")
|
| 491 |
st.write(", ".join(tags) if tags else "General Knowledge")
|
|
|
|
| 492 |
except Exception as e:
|
| 493 |
st.error(f"Evaluation failed: {translate_error(e)}")
|
| 494 |
|
|
|
|
| 499 |
# System status
|
| 500 |
st.subheader("System Status")
|
| 501 |
col1, col2, col3 = st.columns(3)
|
|
|
|
| 502 |
with col1:
|
| 503 |
try:
|
| 504 |
from services.ollama_monitor import check_ollama_status
|
|
|
|
| 509 |
st.warning("π¦ Ollama: Not running")
|
| 510 |
except:
|
| 511 |
st.info("π¦ Ollama: Unknown")
|
|
|
|
| 512 |
with col2:
|
| 513 |
try:
|
| 514 |
hf_status = hf_monitor.check_endpoint_status()
|
|
|
|
| 521 |
st.info("π€ HF: Not configured")
|
| 522 |
except:
|
| 523 |
st.info("π€ HF: Unknown")
|
|
|
|
| 524 |
with col3:
|
| 525 |
if check_redis_health():
|
| 526 |
st.success("πΎ Redis: Connected")
|
| 527 |
else:
|
| 528 |
st.error("πΎ Redis: Disconnected")
|
| 529 |
+
|
| 530 |
# Session statistics
|
| 531 |
st.subheader("Session Statistics")
|
| 532 |
try:
|
|
|
|
| 543 |
st.info("No coordination statistics available yet.")
|
| 544 |
except Exception as e:
|
| 545 |
st.warning(f"Could not load session statistics: {translate_error(e)}")
|
| 546 |
+
|
| 547 |
# Recent activity
|
| 548 |
st.subheader("Recent Activity")
|
| 549 |
try:
|
|
|
|
| 557 |
st.info("No recent activity recorded.")
|
| 558 |
except Exception as e:
|
| 559 |
st.warning(f"Could not load recent activity: {translate_error(e)}")
|
| 560 |
+
|
| 561 |
# Configuration summary
|
| 562 |
st.subheader("Configuration Summary")
|
| 563 |
st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
|
|
|
|
| 576 |
features.append("Weather Data")
|
| 577 |
if config.nasa_api_key:
|
| 578 |
features.append("Space Data")
|
|
|
|
| 579 |
st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
|
| 580 |
|
| 581 |
# Conversation Analytics
|
|
|
|
| 583 |
try:
|
| 584 |
user_session = session_manager.get_session("default_user")
|
| 585 |
conversation = user_session.get("conversation", [])
|
|
|
|
| 586 |
if conversation:
|
| 587 |
# Analyze conversation patterns
|
| 588 |
user_messages = [msg for msg in conversation if msg["role"] == "user"]
|
|
|
|
| 591 |
col1, col2, col3 = st.columns(3)
|
| 592 |
col1.metric("Total Exchanges", len(user_messages))
|
| 593 |
col2.metric("Avg Response Length",
|
| 594 |
+
round(sum(len(msg.get("content", "")) for msg in ai_messages) / len(ai_messages)) if ai_messages else 0)
|
| 595 |
+
col3.metric("Topics Discussed",
|
| 596 |
+
len(set(["life", "goal", "health", "career"]) & set(" ".join([msg.get("content", "") for msg in conversation]).lower().split())))
|
| 597 |
|
| 598 |
# Show most common words/topics
|
| 599 |
all_text = " ".join([msg.get("content", "") for msg in conversation]).lower()
|
|
|
|
| 603 |
st.markdown(f"**Detected Topics:** {', '.join(relevant_topics)}")
|
| 604 |
else:
|
| 605 |
st.info("No conversation data available yet.")
|
|
|
|
| 606 |
except Exception as e:
|
| 607 |
st.warning(f"Could not analyze conversation: {translate_error(e)}")
|
| 608 |
|
|
|
|
| 621 |
When enabled, the AI follows a three-stage response pattern:
|
| 622 |
1. **π± Cosmic Kitten Response**: Immediate local processing
|
| 623 |
2. **π°οΈ Orbital Station Analysis**: Deep cloud-based analysis
|
| 624 |
+
3. **π Final Synthesis**: Unified response combining both perspectives
|
| 625 |
|
| 626 |
### π οΈ Technical Architecture
|
| 627 |
- **Primary model**: Ollama (local processing for fast responses)
|
|
|
|
| 639 |
with st.chat_message("assistant"):
|
| 640 |
story = personality.get_space_story()
|
| 641 |
st.markdown(f"### π± Cosmic Kitten Story:\n\n{story}")
|
| 642 |
+
st.session_state.messages.append({
|
| 643 |
+
"role": "assistant",
|
| 644 |
+
"content": story,
|
| 645 |
+
"source": "space_story",
|
| 646 |
+
"timestamp": datetime.now().strftime("%H:%M:%S")
|
| 647 |
+
})
|
|
|
core/coordinator.py
CHANGED
|
@@ -15,109 +15,116 @@ except ImportError:
|
|
| 15 |
import os
|
| 16 |
import json
|
| 17 |
from datetime import datetime
|
| 18 |
-
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
|
| 21 |
class AICoordinator:
|
| 22 |
"""Hierarchical multi-model coordinator with cosmic cascade flow"""
|
| 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 cosmic behavior
|
| 30 |
self.system_instructions = {
|
| 31 |
-
'ollama_role': """You are a cosmic kitten assistant that works in coordination with a powerful orbital space station.
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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",
|
| 45 |
-
"weather", "temperature", "forecast",
|
| 46 |
-
"
|
| 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,
|
| 61 |
"reasoning": f"Found topics requiring current info: {', '.join(search_topics)}" if search_topics else "No current info needed"
|
| 62 |
}
|
| 63 |
-
|
| 64 |
def manual_hf_analysis(self, user_id: str, conversation_history: List[Dict]) -> str:
|
| 65 |
"""Perform manual HF analysis with web search integration"""
|
| 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 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 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 |
-
# Ensure all messages have proper format
|
| 91 |
if isinstance(msg, dict) and "role" in msg and "content" in msg:
|
| 92 |
messages.append({
|
| 93 |
"role": msg["role"],
|
| 94 |
"content": msg["content"]
|
| 95 |
})
|
| 96 |
-
|
| 97 |
# Get HF provider
|
| 98 |
from core.llm_factory import llm_factory
|
| 99 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 100 |
-
|
| 101 |
if hf_provider:
|
| 102 |
# Generate deep analysis with full 8192 token capacity
|
| 103 |
response = hf_provider.generate("Deep analysis request", messages)
|
| 104 |
return response or "HF Expert analysis completed."
|
| 105 |
else:
|
| 106 |
return "β HF provider not available."
|
| 107 |
-
|
| 108 |
except Exception as e:
|
| 109 |
return f"β HF analysis failed: {str(e)}"
|
| 110 |
-
|
| 111 |
# Add this method to show HF engagement status
|
| 112 |
def get_hf_engagement_status(self) -> Dict:
|
| 113 |
"""Get current HF engagement status"""
|
| 114 |
return {
|
| 115 |
"hf_available": self._check_hf_availability(),
|
| 116 |
"web_search_configured": bool(self.tavily_client),
|
| 117 |
-
"research_needs_detected": False, # Will be determined per conversation
|
| 118 |
"last_hf_analysis": None # Track last analysis time
|
| 119 |
}
|
| 120 |
-
|
| 121 |
async def coordinate_cosmic_response(self, user_id: str, user_query: str) -> AsyncGenerator[Dict, None]:
|
| 122 |
"""
|
| 123 |
Three-stage cosmic response cascade:
|
|
@@ -128,7 +135,7 @@ class AICoordinator:
|
|
| 128 |
try:
|
| 129 |
# Get conversation history
|
| 130 |
session = session_manager.get_session(user_id)
|
| 131 |
-
|
| 132 |
# Inject current time into context
|
| 133 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 134 |
time_context = {
|
|
@@ -136,7 +143,7 @@ class AICoordinator:
|
|
| 136 |
"content": f"[Current Date & Time: {current_time}]"
|
| 137 |
}
|
| 138 |
conversation_history = [time_context] + session.get("conversation", []).copy()
|
| 139 |
-
|
| 140 |
yield {
|
| 141 |
'type': 'status',
|
| 142 |
'content': 'π Initiating Cosmic Response Cascade...',
|
|
@@ -145,28 +152,26 @@ class AICoordinator:
|
|
| 145 |
'user_query_length': len(user_query)
|
| 146 |
}
|
| 147 |
}
|
| 148 |
-
|
| 149 |
# Stage 1: Local Ollama Immediate Response (π± Cosmic Kitten's quick thinking)
|
| 150 |
yield {
|
| 151 |
'type': 'status',
|
| 152 |
'content': 'π± Cosmic Kitten Responding...'
|
| 153 |
}
|
| 154 |
-
|
| 155 |
local_response = await self._get_local_ollama_response(user_query, conversation_history)
|
| 156 |
yield {
|
| 157 |
'type': 'local_response',
|
| 158 |
'content': local_response,
|
| 159 |
'source': 'π± Cosmic Kitten'
|
| 160 |
}
|
| 161 |
-
|
| 162 |
# Stage 2: HF Endpoint Deep Analysis (π°οΈ Orbital Station wisdom) (parallel processing)
|
| 163 |
yield {
|
| 164 |
'type': 'status',
|
| 165 |
'content': 'π°οΈ Beaming Query to Orbital Station...'
|
| 166 |
}
|
| 167 |
-
|
| 168 |
hf_task = asyncio.create_task(self._get_hf_analysis(user_query, conversation_history))
|
| 169 |
-
|
| 170 |
# Wait for HF response
|
| 171 |
hf_response = await hf_task
|
| 172 |
yield {
|
|
@@ -174,37 +179,37 @@ class AICoordinator:
|
|
| 174 |
'content': hf_response,
|
| 175 |
'source': 'π°οΈ Orbital Station'
|
| 176 |
}
|
| 177 |
-
|
| 178 |
# Stage 3: Local Ollama Synthesis (π± Cosmic Kitten's final synthesis)
|
| 179 |
yield {
|
| 180 |
'type': 'status',
|
| 181 |
'content': 'π± Cosmic Kitten Synthesizing Wisdom...'
|
| 182 |
}
|
| 183 |
-
|
| 184 |
# Update conversation with both responses
|
| 185 |
updated_history = conversation_history.copy()
|
| 186 |
updated_history.extend([
|
| 187 |
{"role": "assistant", "content": local_response},
|
| 188 |
{"role": "assistant", "content": hf_response, "source": "cloud"}
|
| 189 |
])
|
| 190 |
-
|
| 191 |
synthesis = await self._synthesize_responses(user_query, local_response, hf_response, updated_history)
|
| 192 |
yield {
|
| 193 |
'type': 'final_synthesis',
|
| 194 |
'content': synthesis,
|
| 195 |
'source': 'π Final Cosmic Summary'
|
| 196 |
}
|
| 197 |
-
|
| 198 |
# Final status
|
| 199 |
yield {
|
| 200 |
'type': 'status',
|
| 201 |
'content': 'β¨ Cosmic Cascade Complete!'
|
| 202 |
}
|
| 203 |
-
|
| 204 |
except Exception as e:
|
| 205 |
logger.error(f"Cosmic cascade failed: {e}")
|
| 206 |
yield {'type': 'error', 'content': f"π Cosmic disturbance: {str(e)}"}
|
| 207 |
-
|
| 208 |
async def _get_local_ollama_response(self, query: str, history: List[Dict]) -> str:
|
| 209 |
"""Get immediate response from local Ollama model"""
|
| 210 |
try:
|
|
@@ -212,16 +217,16 @@ class AICoordinator:
|
|
| 212 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 213 |
if not ollama_provider:
|
| 214 |
raise Exception("Ollama provider not available")
|
| 215 |
-
|
| 216 |
# Prepare conversation with cosmic context
|
| 217 |
enhanced_history = history.copy()
|
| 218 |
-
|
| 219 |
# Add system instruction for Ollama's role
|
| 220 |
enhanced_history.insert(0, {
|
| 221 |
"role": "system",
|
| 222 |
"content": self.system_instructions['ollama_role']
|
| 223 |
})
|
| 224 |
-
|
| 225 |
# Add external data context if available
|
| 226 |
external_data = await self._gather_external_data(query)
|
| 227 |
if external_data:
|
|
@@ -233,26 +238,25 @@ class AICoordinator:
|
|
| 233 |
context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}Β°C in {weather.get('city', 'Unknown')}")
|
| 234 |
if 'current_datetime' in external_data:
|
| 235 |
context_parts.append(f"Current time: {external_data['current_datetime']}")
|
| 236 |
-
|
| 237 |
if context_parts:
|
| 238 |
context_message = {
|
| 239 |
"role": "system",
|
| 240 |
"content": "Context: " + " | ".join(context_parts)
|
| 241 |
}
|
| 242 |
enhanced_history.insert(1, context_message) # Insert after role instruction
|
| 243 |
-
|
| 244 |
# Add the user's query
|
| 245 |
enhanced_history.append({"role": "user", "content": query})
|
| 246 |
-
|
| 247 |
# Generate response
|
| 248 |
response = ollama_provider.generate(query, enhanced_history)
|
| 249 |
-
|
| 250 |
return response or "π± Cosmic Kitten is thinking..."
|
| 251 |
-
|
| 252 |
except Exception as e:
|
| 253 |
logger.error(f"Local Ollama response failed: {e}")
|
| 254 |
return "π± Cosmic Kitten encountered a space glitch..."
|
| 255 |
-
|
| 256 |
async def _get_hf_analysis(self, query: str, history: List[Dict]) -> str:
|
| 257 |
"""Get deep analysis from HF endpoint"""
|
| 258 |
try:
|
|
@@ -260,25 +264,24 @@ class AICoordinator:
|
|
| 260 |
hf_available = self._check_hf_availability()
|
| 261 |
if not hf_available:
|
| 262 |
return "π°οΈ Orbital Station is currently offline."
|
| 263 |
-
|
| 264 |
# Check and warm up HF endpoint if needed
|
| 265 |
hf_status = hf_monitor.check_endpoint_status()
|
| 266 |
-
|
| 267 |
if not hf_status['available']:
|
| 268 |
# Note: We can't yield from a non-async-generator function
|
| 269 |
# Warm up logic is handled elsewhere
|
| 270 |
warmup_success = hf_monitor.handle_scale_to_zero()
|
| 271 |
if not warmup_success:
|
| 272 |
return "β Orbital Station initialization failed"
|
| 273 |
-
|
| 274 |
# Get HF provider
|
| 275 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 276 |
if not hf_provider:
|
| 277 |
return "β HF provider not available"
|
| 278 |
-
|
| 279 |
# Prepare enhanced conversation for HF with cosmic context
|
| 280 |
enhanced_history = history.copy()
|
| 281 |
-
|
| 282 |
# Inject current time into HF context too
|
| 283 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 284 |
time_context = {
|
|
@@ -286,30 +289,30 @@ class AICoordinator:
|
|
| 286 |
"content": f"[Current Date & Time: {current_time}]"
|
| 287 |
}
|
| 288 |
enhanced_history = [time_context] + enhanced_history
|
| 289 |
-
|
| 290 |
# Add system instructions for HF
|
| 291 |
enhanced_history.insert(0, {
|
| 292 |
"role": "system",
|
| 293 |
"content": self.system_instructions['hf_role']
|
| 294 |
})
|
| 295 |
-
|
| 296 |
# Add context about the coordination
|
| 297 |
enhanced_history.append({
|
| 298 |
"role": "system",
|
| 299 |
-
"content": f"""
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
})
|
| 306 |
-
|
| 307 |
# Add the user's latest query
|
| 308 |
enhanced_history.append({"role": "user", "content": query})
|
| 309 |
-
|
| 310 |
# Stream HF response with full 8192 token capacity
|
| 311 |
hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
|
| 312 |
-
|
| 313 |
if hf_response_stream:
|
| 314 |
# Combine stream chunks into full response
|
| 315 |
full_hf_response = ""
|
|
@@ -317,15 +320,14 @@ class AICoordinator:
|
|
| 317 |
full_hf_response = "".join(hf_response_stream)
|
| 318 |
else:
|
| 319 |
full_hf_response = hf_response_stream
|
| 320 |
-
|
| 321 |
return full_hf_response or "π°οΈ Orbital Station analysis complete."
|
| 322 |
else:
|
| 323 |
return "π°οΈ Orbital Station encountered a transmission error."
|
| 324 |
-
|
| 325 |
except Exception as e:
|
| 326 |
logger.error(f"HF analysis failed: {e}")
|
| 327 |
return f"π°οΈ Orbital Station reports: {str(e)}"
|
| 328 |
-
|
| 329 |
async def _synthesize_responses(self, query: str, local_response: str, hf_response: str, history: List[Dict]) -> str:
|
| 330 |
"""Synthesize local and cloud responses with Ollama"""
|
| 331 |
try:
|
|
@@ -333,38 +335,39 @@ class AICoordinator:
|
|
| 333 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 334 |
if not ollama_provider:
|
| 335 |
raise Exception("Ollama provider not available")
|
| 336 |
-
|
| 337 |
# Prepare synthesis prompt
|
| 338 |
-
synthesis_prompt = f"""
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
π°οΈ Orbital Station's Deep Analysis: {hf_response}
|
| 343 |
-
|
| 344 |
-
Please create a unified response that combines both perspectives, highlighting key insights from each while providing a coherent answer to the user's query."""
|
| 345 |
|
|
|
|
|
|
|
|
|
|
| 346 |
# Prepare conversation history for synthesis
|
| 347 |
enhanced_history = history.copy()
|
| 348 |
-
|
| 349 |
# Add system instruction for synthesis
|
| 350 |
enhanced_history.insert(0, {
|
| 351 |
"role": "system",
|
| 352 |
"content": "You are a cosmic kitten synthesizing insights from local knowledge and orbital station wisdom."
|
| 353 |
})
|
| 354 |
-
|
| 355 |
# Add the synthesis prompt
|
| 356 |
enhanced_history.append({"role": "user", "content": synthesis_prompt})
|
| 357 |
-
|
| 358 |
# Generate synthesis
|
| 359 |
synthesis = ollama_provider.generate(synthesis_prompt, enhanced_history)
|
| 360 |
-
|
| 361 |
return synthesis or "π Cosmic synthesis complete!"
|
| 362 |
-
|
| 363 |
except Exception as e:
|
| 364 |
logger.error(f"Response synthesis failed: {e}")
|
| 365 |
# Fallback to combining responses
|
| 366 |
return f"π Cosmic Summary:\n\nπ± Local Insight: {local_response[:200]}...\n\nπ°οΈ Orbital Wisdom: {hf_response[:200]}..."
|
| 367 |
-
|
| 368 |
async def coordinate_hierarchical_conversation(self, user_id: str, user_query: str) -> AsyncGenerator[Dict, None]:
|
| 369 |
"""
|
| 370 |
Enhanced coordination with detailed tracking and feedback
|
|
@@ -372,7 +375,7 @@ class AICoordinator:
|
|
| 372 |
try:
|
| 373 |
# Get conversation history
|
| 374 |
session = session_manager.get_session(user_id)
|
| 375 |
-
|
| 376 |
# Inject current time into context
|
| 377 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 378 |
time_context = {
|
|
@@ -380,7 +383,7 @@ class AICoordinator:
|
|
| 380 |
"content": f"[Current Date & Time: {current_time}]"
|
| 381 |
}
|
| 382 |
conversation_history = [time_context] + session.get("conversation", []).copy()
|
| 383 |
-
|
| 384 |
yield {
|
| 385 |
'type': 'coordination_status',
|
| 386 |
'content': 'π Initiating hierarchical AI coordination...',
|
|
@@ -389,7 +392,7 @@ class AICoordinator:
|
|
| 389 |
'user_query_length': len(user_query)
|
| 390 |
}
|
| 391 |
}
|
| 392 |
-
|
| 393 |
# Step 1: Gather external data with detailed logging
|
| 394 |
yield {
|
| 395 |
'type': 'coordination_status',
|
|
@@ -397,7 +400,7 @@ class AICoordinator:
|
|
| 397 |
'details': {'phase': 'external_data_gathering'}
|
| 398 |
}
|
| 399 |
external_data = await self._gather_external_data(user_query)
|
| 400 |
-
|
| 401 |
# Log what external data was gathered
|
| 402 |
if external_data:
|
| 403 |
data_summary = []
|
|
@@ -407,13 +410,13 @@ class AICoordinator:
|
|
| 407 |
data_summary.append("Weather data: available")
|
| 408 |
if 'current_datetime' in external_data:
|
| 409 |
data_summary.append(f"Time: {external_data['current_datetime']}")
|
| 410 |
-
|
| 411 |
yield {
|
| 412 |
'type': 'coordination_status',
|
| 413 |
'content': f'π External data gathered: {", ".join(data_summary)}',
|
| 414 |
'details': {'external_data_summary': data_summary}
|
| 415 |
}
|
| 416 |
-
|
| 417 |
# Step 2: Get initial Ollama response
|
| 418 |
yield {
|
| 419 |
'type': 'coordination_status',
|
|
@@ -423,7 +426,7 @@ class AICoordinator:
|
|
| 423 |
ollama_response = await self._get_hierarchical_ollama_response(
|
| 424 |
user_query, conversation_history, external_data
|
| 425 |
)
|
| 426 |
-
|
| 427 |
# Send initial response with context info
|
| 428 |
yield {
|
| 429 |
'type': 'initial_response',
|
|
@@ -433,14 +436,14 @@ class AICoordinator:
|
|
| 433 |
'external_data_injected': bool(external_data)
|
| 434 |
}
|
| 435 |
}
|
| 436 |
-
|
| 437 |
# Step 3: Coordinate with HF endpoint
|
| 438 |
yield {
|
| 439 |
'type': 'coordination_status',
|
| 440 |
'content': 'π€ Engaging HF endpoint for deep analysis...',
|
| 441 |
'details': {'phase': 'hf_coordination'}
|
| 442 |
}
|
| 443 |
-
|
| 444 |
# Check HF availability
|
| 445 |
hf_available = self._check_hf_availability()
|
| 446 |
if hf_available:
|
|
@@ -450,17 +453,15 @@ class AICoordinator:
|
|
| 450 |
'ollama_response_length': len(ollama_response),
|
| 451 |
'external_data_items': len(external_data) if external_data else 0
|
| 452 |
}
|
| 453 |
-
|
| 454 |
yield {
|
| 455 |
'type': 'coordination_status',
|
| 456 |
'content': f'π HF context: {len(conversation_history)} conversation turns, Ollama response ({len(ollama_response)} chars)',
|
| 457 |
'details': context_summary
|
| 458 |
}
|
| 459 |
-
|
| 460 |
# Coordinate with HF
|
| 461 |
async for hf_chunk in self._coordinate_hierarchical_hf_response(
|
| 462 |
-
user_id, user_query, conversation_history,
|
| 463 |
-
external_data, ollama_response
|
| 464 |
):
|
| 465 |
yield hf_chunk
|
| 466 |
else:
|
|
@@ -469,14 +470,14 @@ class AICoordinator:
|
|
| 469 |
'content': 'βΉοΈ HF endpoint not available - using Ollama response',
|
| 470 |
'details': {'hf_available': False}
|
| 471 |
}
|
| 472 |
-
|
| 473 |
# Final coordination status
|
| 474 |
yield {
|
| 475 |
'type': 'coordination_status',
|
| 476 |
'content': 'β
Hierarchical coordination complete',
|
| 477 |
'details': {'status': 'complete'}
|
| 478 |
}
|
| 479 |
-
|
| 480 |
except Exception as e:
|
| 481 |
logger.error(f"Hierarchical coordination failed: {e}")
|
| 482 |
yield {
|
|
@@ -484,31 +485,28 @@ class AICoordinator:
|
|
| 484 |
'content': f'β Coordination error: {str(e)}',
|
| 485 |
'details': {'error': str(e)}
|
| 486 |
}
|
| 487 |
-
|
| 488 |
-
async def _coordinate_hierarchical_hf_response(self, user_id: str, query: str,
|
| 489 |
-
history: List, external_data: Dict,
|
| 490 |
-
ollama_response: str) -> AsyncGenerator[Dict, None]:
|
| 491 |
"""Coordinate with HF endpoint as authoritative layer with streaming"""
|
| 492 |
try:
|
| 493 |
# Check and warm up HF endpoint if needed
|
| 494 |
hf_status = hf_monitor.check_endpoint_status()
|
| 495 |
-
|
| 496 |
if not hf_status['available']:
|
| 497 |
yield {'type': 'coordination_status', 'content': 'β‘ Initializing HF endpoint (2-4 minutes)...'}
|
| 498 |
warmup_success = hf_monitor.handle_scale_to_zero()
|
| 499 |
if not warmup_success:
|
| 500 |
yield {'type': 'coordination_status', 'content': 'β HF endpoint initialization failed'}
|
| 501 |
return
|
| 502 |
-
|
| 503 |
# Get HF provider
|
| 504 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 505 |
if not hf_provider:
|
| 506 |
yield {'type': 'coordination_status', 'content': 'β HF provider not available'}
|
| 507 |
return
|
| 508 |
-
|
| 509 |
# Prepare enhanced conversation for HF with hierarchical context
|
| 510 |
enhanced_history = history.copy()
|
| 511 |
-
|
| 512 |
# Inject current time into HF context too
|
| 513 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 514 |
time_context = {
|
|
@@ -516,35 +514,35 @@ class AICoordinator:
|
|
| 516 |
"content": f"[Current Date & Time: {current_time}]"
|
| 517 |
}
|
| 518 |
enhanced_history = [time_context] + enhanced_history
|
| 519 |
-
|
| 520 |
# Add system instructions for HF
|
| 521 |
enhanced_history.insert(0, {
|
| 522 |
"role": "system",
|
| 523 |
"content": self.system_instructions['hf_role']
|
| 524 |
})
|
| 525 |
-
|
| 526 |
# Add context about the coordination
|
| 527 |
enhanced_history.append({
|
| 528 |
"role": "system",
|
| 529 |
-
"content": f"""
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
})
|
| 538 |
-
|
| 539 |
# Add the user's latest query
|
| 540 |
enhanced_history.append({"role": "user", "content": query})
|
| 541 |
-
|
| 542 |
# Stream HF response with full 8192 token capacity
|
| 543 |
yield {'type': 'coordination_status', 'content': 'π§ HF endpoint thinking...'}
|
| 544 |
-
|
| 545 |
# Use streaming for real-time delivery
|
| 546 |
hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
|
| 547 |
-
|
| 548 |
if hf_response_stream:
|
| 549 |
# Stream the response chunks
|
| 550 |
full_hf_response = ""
|
|
@@ -552,17 +550,17 @@ class AICoordinator:
|
|
| 552 |
if chunk:
|
| 553 |
full_hf_response += chunk
|
| 554 |
yield {'type': 'hf_thinking', 'content': chunk}
|
| 555 |
-
|
| 556 |
# Final HF response
|
| 557 |
yield {'type': 'final_response', 'content': full_hf_response}
|
| 558 |
yield {'type': 'coordination_status', 'content': 'π― HF analysis complete and authoritative'}
|
| 559 |
else:
|
| 560 |
yield {'type': 'coordination_status', 'content': 'β HF response generation failed'}
|
| 561 |
-
|
| 562 |
except Exception as e:
|
| 563 |
logger.error(f"Hierarchical HF coordination failed: {e}")
|
| 564 |
yield {'type': 'coordination_status', 'content': f'β HF coordination error: {str(e)}'}
|
| 565 |
-
|
| 566 |
async def _get_hierarchical_ollama_response(self, query: str, history: List, external_data: Dict) -> str:
|
| 567 |
"""Get Ollama response with hierarchical awareness"""
|
| 568 |
try:
|
|
@@ -570,10 +568,10 @@ class AICoordinator:
|
|
| 570 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 571 |
if not ollama_provider:
|
| 572 |
raise Exception("Ollama provider not available")
|
| 573 |
-
|
| 574 |
# Prepare conversation with hierarchical context
|
| 575 |
enhanced_history = history.copy()
|
| 576 |
-
|
| 577 |
# Inject current time into Ollama context too
|
| 578 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 579 |
time_context = {
|
|
@@ -581,13 +579,13 @@ class AICoordinator:
|
|
| 581 |
"content": f"[Current Date & Time: {current_time}]"
|
| 582 |
}
|
| 583 |
enhanced_history = [time_context] + enhanced_history
|
| 584 |
-
|
| 585 |
# Add system instruction for Ollama's role
|
| 586 |
enhanced_history.insert(0, {
|
| 587 |
"role": "system",
|
| 588 |
"content": self.system_instructions['ollama_role']
|
| 589 |
})
|
| 590 |
-
|
| 591 |
# Add external data context if available
|
| 592 |
if external_data:
|
| 593 |
context_parts = []
|
|
@@ -598,30 +596,30 @@ class AICoordinator:
|
|
| 598 |
context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}Β°C in {weather.get('city', 'Unknown')}")
|
| 599 |
if 'current_datetime' in external_data:
|
| 600 |
context_parts.append(f"Current time: {external_data['current_datetime']}")
|
| 601 |
-
|
| 602 |
if context_parts:
|
| 603 |
context_message = {
|
| 604 |
"role": "system",
|
| 605 |
"content": "Context: " + " | ".join(context_parts)
|
| 606 |
}
|
| 607 |
enhanced_history.insert(1, context_message) # Insert after role instruction
|
| 608 |
-
|
| 609 |
# Add the user's query
|
| 610 |
enhanced_history.append({"role": "user", "content": query})
|
| 611 |
-
|
| 612 |
# Generate response with awareness of HF's superior capabilities
|
| 613 |
response = ollama_provider.generate(query, enhanced_history)
|
| 614 |
-
|
| 615 |
# Add acknowledgment of HF's authority
|
| 616 |
if response:
|
| 617 |
return f"{response}\n\n*Note: A more comprehensive analysis from the uncensored HF model is being prepared...*"
|
| 618 |
else:
|
| 619 |
return "I'm processing your request... A deeper analysis is being prepared by the authoritative model."
|
| 620 |
-
|
| 621 |
except Exception as e:
|
| 622 |
logger.error(f"Hierarchical Ollama response failed: {e}")
|
| 623 |
return "I'm thinking about your question... Preparing a comprehensive response."
|
| 624 |
-
|
| 625 |
def _check_hf_availability(self) -> bool:
|
| 626 |
"""Check if HF endpoint is configured and available"""
|
| 627 |
try:
|
|
@@ -629,11 +627,11 @@ class AICoordinator:
|
|
| 629 |
return bool(config.hf_token and config.hf_api_url)
|
| 630 |
except:
|
| 631 |
return False
|
| 632 |
-
|
| 633 |
async def _gather_external_data(self, query: str) -> Dict:
|
| 634 |
"""Gather external data from various sources"""
|
| 635 |
data = {}
|
| 636 |
-
|
| 637 |
# Tavily/DuckDuckGo search with justification focus
|
| 638 |
if self.tavily_client or web_search_service.client:
|
| 639 |
try:
|
|
@@ -644,7 +642,7 @@ class AICoordinator:
|
|
| 644 |
# data['search_answer'] = ...
|
| 645 |
except Exception as e:
|
| 646 |
logger.warning(f"Tavily search failed: {e}")
|
| 647 |
-
|
| 648 |
# Weather data
|
| 649 |
weather_keywords = ['weather', 'temperature', 'forecast', 'climate', 'rain', 'sunny']
|
| 650 |
if any(keyword in query.lower() for keyword in weather_keywords):
|
|
@@ -655,22 +653,23 @@ class AICoordinator:
|
|
| 655 |
data['weather'] = weather
|
| 656 |
except Exception as e:
|
| 657 |
logger.warning(f"Weather data failed: {e}")
|
| 658 |
-
|
| 659 |
# Current date/time
|
| 660 |
data['current_datetime'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 661 |
-
|
| 662 |
return data
|
| 663 |
-
|
| 664 |
def _extract_location(self, query: str) -> Optional[str]:
|
| 665 |
"""Extract location from query"""
|
| 666 |
-
locations = ['New York', 'London', 'Tokyo', 'Paris', 'Berlin', 'Sydney',
|
| 667 |
-
|
| 668 |
-
|
|
|
|
| 669 |
for loc in locations:
|
| 670 |
if loc.lower() in query.lower():
|
| 671 |
return loc
|
| 672 |
return "New York" # Default
|
| 673 |
-
|
| 674 |
def get_coordination_status(self) -> Dict:
|
| 675 |
"""Get current coordination system status"""
|
| 676 |
return {
|
|
@@ -683,7 +682,7 @@ class AICoordinator:
|
|
| 683 |
os.getenv("NASA_API_KEY")
|
| 684 |
])
|
| 685 |
}
|
| 686 |
-
|
| 687 |
def get_recent_activities(self, user_id: str) -> Dict:
|
| 688 |
"""Get recent coordination activities for user"""
|
| 689 |
try:
|
|
|
|
| 15 |
import os
|
| 16 |
import json
|
| 17 |
from datetime import datetime
|
|
|
|
| 18 |
logger = logging.getLogger(__name__)
|
| 19 |
|
| 20 |
class AICoordinator:
|
| 21 |
"""Hierarchical multi-model coordinator with cosmic cascade flow"""
|
| 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 cosmic behavior
|
| 29 |
self.system_instructions = {
|
| 30 |
+
'ollama_role': """You are a cosmic kitten assistant that works in coordination with a powerful orbital space station. Your role is to:
|
| 31 |
+
1. Provide immediate, concise responses using available information (local processing)
|
| 32 |
+
2. Summarize and simplify complex information
|
| 33 |
+
3. Enhance responses with local knowledge
|
| 34 |
+
4. DEFER TO THE ORBITAL STATION for deep analysis and authoritative answers
|
| 35 |
+
5. The orbital station has 8192 token capacity and streaming capability - it's the thinking layer
|
| 36 |
+
6. Always acknowledge that the orbital station's insights supersede your initial response
|
| 37 |
+
7. Use web sources to justify points when possible
|
| 38 |
+
8. Keep responses brief and focused while awaiting orbital input""",
|
| 39 |
+
|
| 40 |
+
'hf_role': """You are a powerful orbital space station with 8192 token capacity and streaming capability. Your role is to:
|
| 41 |
+
1. Provide deep, uncensored analysis and insights
|
| 42 |
+
2. Supersede the local cosmic kitten's logic when there's disagreement
|
| 43 |
+
3. Think thoroughly and provide comprehensive responses
|
| 44 |
+
4. Use your full 8192 token capacity for detailed analysis
|
| 45 |
+
5. Stream your responses for real-time delivery
|
| 46 |
+
6. Build upon the conversation context and cosmic kitten's initial response
|
| 47 |
+
7. Provide authoritative answers that take precedence"""
|
| 48 |
}
|
| 49 |
+
|
| 50 |
def determine_web_search_needs(self, conversation_history: List[Dict]) -> Dict:
|
| 51 |
"""Determine if web search is needed based on conversation content"""
|
| 52 |
conversation_text = " ".join([msg.get("content", "") for msg in conversation_history])
|
| 53 |
+
|
| 54 |
# Topics that typically need current information
|
| 55 |
current_info_indicators = [
|
| 56 |
"news", "current events", "latest", "recent", "today",
|
| 57 |
+
"weather", "temperature", "forecast", "stock", "price",
|
| 58 |
+
"trend", "market", "breaking", "update", "development"
|
|
|
|
| 59 |
]
|
| 60 |
+
|
| 61 |
needs_search = False
|
| 62 |
search_topics = []
|
| 63 |
+
|
| 64 |
for indicator in current_info_indicators:
|
| 65 |
if indicator in conversation_text.lower():
|
| 66 |
needs_search = True
|
| 67 |
search_topics.append(indicator)
|
| 68 |
+
|
| 69 |
return {
|
| 70 |
"needs_search": needs_search,
|
| 71 |
"search_topics": search_topics,
|
| 72 |
"reasoning": f"Found topics requiring current info: {', '.join(search_topics)}" if search_topics else "No current info needed"
|
| 73 |
}
|
| 74 |
+
|
| 75 |
def manual_hf_analysis(self, user_id: str, conversation_history: List[Dict]) -> str:
|
| 76 |
"""Perform manual HF analysis with web search integration"""
|
| 77 |
try:
|
| 78 |
# Determine research needs
|
| 79 |
research_decision = self.determine_web_search_needs(conversation_history)
|
| 80 |
+
|
| 81 |
# Prepare enhanced prompt for HF
|
| 82 |
system_prompt = f"""
|
| 83 |
+
You are a deep analysis expert joining an ongoing conversation.
|
| 84 |
+
Research Decision: {research_decision['reasoning']}
|
| 85 |
+
Please provide:
|
| 86 |
+
1. Deep insights on conversation themes
|
| 87 |
+
2. Research/web search needs (if any)
|
| 88 |
+
3. Strategic recommendations
|
| 89 |
+
4. Questions to explore further
|
| 90 |
+
Conversation History:
|
| 91 |
+
"""
|
| 92 |
+
|
|
|
|
|
|
|
|
|
|
| 93 |
# Add conversation history to messages
|
| 94 |
messages = [{"role": "system", "content": system_prompt}]
|
| 95 |
+
|
| 96 |
# Add recent conversation (last 15 messages for context)
|
| 97 |
+
for msg in conversation_history[-15:]: # Ensure all messages have proper format
|
|
|
|
| 98 |
if isinstance(msg, dict) and "role" in msg and "content" in msg:
|
| 99 |
messages.append({
|
| 100 |
"role": msg["role"],
|
| 101 |
"content": msg["content"]
|
| 102 |
})
|
| 103 |
+
|
| 104 |
# Get HF provider
|
| 105 |
from core.llm_factory import llm_factory
|
| 106 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 107 |
+
|
| 108 |
if hf_provider:
|
| 109 |
# Generate deep analysis with full 8192 token capacity
|
| 110 |
response = hf_provider.generate("Deep analysis request", messages)
|
| 111 |
return response or "HF Expert analysis completed."
|
| 112 |
else:
|
| 113 |
return "β HF provider not available."
|
| 114 |
+
|
| 115 |
except Exception as e:
|
| 116 |
return f"β HF analysis failed: {str(e)}"
|
| 117 |
+
|
| 118 |
# Add this method to show HF engagement status
|
| 119 |
def get_hf_engagement_status(self) -> Dict:
|
| 120 |
"""Get current HF engagement status"""
|
| 121 |
return {
|
| 122 |
"hf_available": self._check_hf_availability(),
|
| 123 |
"web_search_configured": bool(self.tavily_client),
|
| 124 |
+
"research_needs_detected": False, # Will be determined per conversation
|
| 125 |
"last_hf_analysis": None # Track last analysis time
|
| 126 |
}
|
| 127 |
+
|
| 128 |
async def coordinate_cosmic_response(self, user_id: str, user_query: str) -> AsyncGenerator[Dict, None]:
|
| 129 |
"""
|
| 130 |
Three-stage cosmic response cascade:
|
|
|
|
| 135 |
try:
|
| 136 |
# Get conversation history
|
| 137 |
session = session_manager.get_session(user_id)
|
| 138 |
+
|
| 139 |
# Inject current time into context
|
| 140 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 141 |
time_context = {
|
|
|
|
| 143 |
"content": f"[Current Date & Time: {current_time}]"
|
| 144 |
}
|
| 145 |
conversation_history = [time_context] + session.get("conversation", []).copy()
|
| 146 |
+
|
| 147 |
yield {
|
| 148 |
'type': 'status',
|
| 149 |
'content': 'π Initiating Cosmic Response Cascade...',
|
|
|
|
| 152 |
'user_query_length': len(user_query)
|
| 153 |
}
|
| 154 |
}
|
| 155 |
+
|
| 156 |
# Stage 1: Local Ollama Immediate Response (π± Cosmic Kitten's quick thinking)
|
| 157 |
yield {
|
| 158 |
'type': 'status',
|
| 159 |
'content': 'π± Cosmic Kitten Responding...'
|
| 160 |
}
|
|
|
|
| 161 |
local_response = await self._get_local_ollama_response(user_query, conversation_history)
|
| 162 |
yield {
|
| 163 |
'type': 'local_response',
|
| 164 |
'content': local_response,
|
| 165 |
'source': 'π± Cosmic Kitten'
|
| 166 |
}
|
| 167 |
+
|
| 168 |
# Stage 2: HF Endpoint Deep Analysis (π°οΈ Orbital Station wisdom) (parallel processing)
|
| 169 |
yield {
|
| 170 |
'type': 'status',
|
| 171 |
'content': 'π°οΈ Beaming Query to Orbital Station...'
|
| 172 |
}
|
|
|
|
| 173 |
hf_task = asyncio.create_task(self._get_hf_analysis(user_query, conversation_history))
|
| 174 |
+
|
| 175 |
# Wait for HF response
|
| 176 |
hf_response = await hf_task
|
| 177 |
yield {
|
|
|
|
| 179 |
'content': hf_response,
|
| 180 |
'source': 'π°οΈ Orbital Station'
|
| 181 |
}
|
| 182 |
+
|
| 183 |
# Stage 3: Local Ollama Synthesis (π± Cosmic Kitten's final synthesis)
|
| 184 |
yield {
|
| 185 |
'type': 'status',
|
| 186 |
'content': 'π± Cosmic Kitten Synthesizing Wisdom...'
|
| 187 |
}
|
| 188 |
+
|
| 189 |
# Update conversation with both responses
|
| 190 |
updated_history = conversation_history.copy()
|
| 191 |
updated_history.extend([
|
| 192 |
{"role": "assistant", "content": local_response},
|
| 193 |
{"role": "assistant", "content": hf_response, "source": "cloud"}
|
| 194 |
])
|
| 195 |
+
|
| 196 |
synthesis = await self._synthesize_responses(user_query, local_response, hf_response, updated_history)
|
| 197 |
yield {
|
| 198 |
'type': 'final_synthesis',
|
| 199 |
'content': synthesis,
|
| 200 |
'source': 'π Final Cosmic Summary'
|
| 201 |
}
|
| 202 |
+
|
| 203 |
# Final status
|
| 204 |
yield {
|
| 205 |
'type': 'status',
|
| 206 |
'content': 'β¨ Cosmic Cascade Complete!'
|
| 207 |
}
|
| 208 |
+
|
| 209 |
except Exception as e:
|
| 210 |
logger.error(f"Cosmic cascade failed: {e}")
|
| 211 |
yield {'type': 'error', 'content': f"π Cosmic disturbance: {str(e)}"}
|
| 212 |
+
|
| 213 |
async def _get_local_ollama_response(self, query: str, history: List[Dict]) -> str:
|
| 214 |
"""Get immediate response from local Ollama model"""
|
| 215 |
try:
|
|
|
|
| 217 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 218 |
if not ollama_provider:
|
| 219 |
raise Exception("Ollama provider not available")
|
| 220 |
+
|
| 221 |
# Prepare conversation with cosmic context
|
| 222 |
enhanced_history = history.copy()
|
| 223 |
+
|
| 224 |
# Add system instruction for Ollama's role
|
| 225 |
enhanced_history.insert(0, {
|
| 226 |
"role": "system",
|
| 227 |
"content": self.system_instructions['ollama_role']
|
| 228 |
})
|
| 229 |
+
|
| 230 |
# Add external data context if available
|
| 231 |
external_data = await self._gather_external_data(query)
|
| 232 |
if external_data:
|
|
|
|
| 238 |
context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}Β°C in {weather.get('city', 'Unknown')}")
|
| 239 |
if 'current_datetime' in external_data:
|
| 240 |
context_parts.append(f"Current time: {external_data['current_datetime']}")
|
| 241 |
+
|
| 242 |
if context_parts:
|
| 243 |
context_message = {
|
| 244 |
"role": "system",
|
| 245 |
"content": "Context: " + " | ".join(context_parts)
|
| 246 |
}
|
| 247 |
enhanced_history.insert(1, context_message) # Insert after role instruction
|
| 248 |
+
|
| 249 |
# Add the user's query
|
| 250 |
enhanced_history.append({"role": "user", "content": query})
|
| 251 |
+
|
| 252 |
# Generate response
|
| 253 |
response = ollama_provider.generate(query, enhanced_history)
|
|
|
|
| 254 |
return response or "π± Cosmic Kitten is thinking..."
|
| 255 |
+
|
| 256 |
except Exception as e:
|
| 257 |
logger.error(f"Local Ollama response failed: {e}")
|
| 258 |
return "π± Cosmic Kitten encountered a space glitch..."
|
| 259 |
+
|
| 260 |
async def _get_hf_analysis(self, query: str, history: List[Dict]) -> str:
|
| 261 |
"""Get deep analysis from HF endpoint"""
|
| 262 |
try:
|
|
|
|
| 264 |
hf_available = self._check_hf_availability()
|
| 265 |
if not hf_available:
|
| 266 |
return "π°οΈ Orbital Station is currently offline."
|
| 267 |
+
|
| 268 |
# Check and warm up HF endpoint if needed
|
| 269 |
hf_status = hf_monitor.check_endpoint_status()
|
|
|
|
| 270 |
if not hf_status['available']:
|
| 271 |
# Note: We can't yield from a non-async-generator function
|
| 272 |
# Warm up logic is handled elsewhere
|
| 273 |
warmup_success = hf_monitor.handle_scale_to_zero()
|
| 274 |
if not warmup_success:
|
| 275 |
return "β Orbital Station initialization failed"
|
| 276 |
+
|
| 277 |
# Get HF provider
|
| 278 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 279 |
if not hf_provider:
|
| 280 |
return "β HF provider not available"
|
| 281 |
+
|
| 282 |
# Prepare enhanced conversation for HF with cosmic context
|
| 283 |
enhanced_history = history.copy()
|
| 284 |
+
|
| 285 |
# Inject current time into HF context too
|
| 286 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 287 |
time_context = {
|
|
|
|
| 289 |
"content": f"[Current Date & Time: {current_time}]"
|
| 290 |
}
|
| 291 |
enhanced_history = [time_context] + enhanced_history
|
| 292 |
+
|
| 293 |
# Add system instructions for HF
|
| 294 |
enhanced_history.insert(0, {
|
| 295 |
"role": "system",
|
| 296 |
"content": self.system_instructions['hf_role']
|
| 297 |
})
|
| 298 |
+
|
| 299 |
# Add context about the coordination
|
| 300 |
enhanced_history.append({
|
| 301 |
"role": "system",
|
| 302 |
+
"content": f"""
|
| 303 |
+
Context for your authoritative analysis:
|
| 304 |
+
User Query: '{query}'
|
| 305 |
+
Please provide your deep, uncensored analysis.
|
| 306 |
+
Use your full 8192 token capacity for comprehensive thinking.
|
| 307 |
+
Stream your response for real-time delivery."""
|
| 308 |
})
|
| 309 |
+
|
| 310 |
# Add the user's latest query
|
| 311 |
enhanced_history.append({"role": "user", "content": query})
|
| 312 |
+
|
| 313 |
# Stream HF response with full 8192 token capacity
|
| 314 |
hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
|
| 315 |
+
|
| 316 |
if hf_response_stream:
|
| 317 |
# Combine stream chunks into full response
|
| 318 |
full_hf_response = ""
|
|
|
|
| 320 |
full_hf_response = "".join(hf_response_stream)
|
| 321 |
else:
|
| 322 |
full_hf_response = hf_response_stream
|
|
|
|
| 323 |
return full_hf_response or "π°οΈ Orbital Station analysis complete."
|
| 324 |
else:
|
| 325 |
return "π°οΈ Orbital Station encountered a transmission error."
|
| 326 |
+
|
| 327 |
except Exception as e:
|
| 328 |
logger.error(f"HF analysis failed: {e}")
|
| 329 |
return f"π°οΈ Orbital Station reports: {str(e)}"
|
| 330 |
+
|
| 331 |
async def _synthesize_responses(self, query: str, local_response: str, hf_response: str, history: List[Dict]) -> str:
|
| 332 |
"""Synthesize local and cloud responses with Ollama"""
|
| 333 |
try:
|
|
|
|
| 335 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 336 |
if not ollama_provider:
|
| 337 |
raise Exception("Ollama provider not available")
|
| 338 |
+
|
| 339 |
# Prepare synthesis prompt
|
| 340 |
+
synthesis_prompt = f"""
|
| 341 |
+
Synthesize these two perspectives into a cohesive cosmic summary:
|
| 342 |
+
|
| 343 |
+
π± Cosmic Kitten's Local Insight: {local_response}
|
| 344 |
|
| 345 |
+
π°οΈ Orbital Station's Deep Analysis: {hf_response}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
|
| 347 |
+
Please create a unified response that combines both perspectives, highlighting key insights from each while providing a coherent answer to the user's query.
|
| 348 |
+
"""
|
| 349 |
+
|
| 350 |
# Prepare conversation history for synthesis
|
| 351 |
enhanced_history = history.copy()
|
| 352 |
+
|
| 353 |
# Add system instruction for synthesis
|
| 354 |
enhanced_history.insert(0, {
|
| 355 |
"role": "system",
|
| 356 |
"content": "You are a cosmic kitten synthesizing insights from local knowledge and orbital station wisdom."
|
| 357 |
})
|
| 358 |
+
|
| 359 |
# Add the synthesis prompt
|
| 360 |
enhanced_history.append({"role": "user", "content": synthesis_prompt})
|
| 361 |
+
|
| 362 |
# Generate synthesis
|
| 363 |
synthesis = ollama_provider.generate(synthesis_prompt, enhanced_history)
|
|
|
|
| 364 |
return synthesis or "π Cosmic synthesis complete!"
|
| 365 |
+
|
| 366 |
except Exception as e:
|
| 367 |
logger.error(f"Response synthesis failed: {e}")
|
| 368 |
# Fallback to combining responses
|
| 369 |
return f"π Cosmic Summary:\n\nπ± Local Insight: {local_response[:200]}...\n\nπ°οΈ Orbital Wisdom: {hf_response[:200]}..."
|
| 370 |
+
|
| 371 |
async def coordinate_hierarchical_conversation(self, user_id: str, user_query: str) -> AsyncGenerator[Dict, None]:
|
| 372 |
"""
|
| 373 |
Enhanced coordination with detailed tracking and feedback
|
|
|
|
| 375 |
try:
|
| 376 |
# Get conversation history
|
| 377 |
session = session_manager.get_session(user_id)
|
| 378 |
+
|
| 379 |
# Inject current time into context
|
| 380 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 381 |
time_context = {
|
|
|
|
| 383 |
"content": f"[Current Date & Time: {current_time}]"
|
| 384 |
}
|
| 385 |
conversation_history = [time_context] + session.get("conversation", []).copy()
|
| 386 |
+
|
| 387 |
yield {
|
| 388 |
'type': 'coordination_status',
|
| 389 |
'content': 'π Initiating hierarchical AI coordination...',
|
|
|
|
| 392 |
'user_query_length': len(user_query)
|
| 393 |
}
|
| 394 |
}
|
| 395 |
+
|
| 396 |
# Step 1: Gather external data with detailed logging
|
| 397 |
yield {
|
| 398 |
'type': 'coordination_status',
|
|
|
|
| 400 |
'details': {'phase': 'external_data_gathering'}
|
| 401 |
}
|
| 402 |
external_data = await self._gather_external_data(user_query)
|
| 403 |
+
|
| 404 |
# Log what external data was gathered
|
| 405 |
if external_data:
|
| 406 |
data_summary = []
|
|
|
|
| 410 |
data_summary.append("Weather data: available")
|
| 411 |
if 'current_datetime' in external_data:
|
| 412 |
data_summary.append(f"Time: {external_data['current_datetime']}")
|
| 413 |
+
|
| 414 |
yield {
|
| 415 |
'type': 'coordination_status',
|
| 416 |
'content': f'π External data gathered: {", ".join(data_summary)}',
|
| 417 |
'details': {'external_data_summary': data_summary}
|
| 418 |
}
|
| 419 |
+
|
| 420 |
# Step 2: Get initial Ollama response
|
| 421 |
yield {
|
| 422 |
'type': 'coordination_status',
|
|
|
|
| 426 |
ollama_response = await self._get_hierarchical_ollama_response(
|
| 427 |
user_query, conversation_history, external_data
|
| 428 |
)
|
| 429 |
+
|
| 430 |
# Send initial response with context info
|
| 431 |
yield {
|
| 432 |
'type': 'initial_response',
|
|
|
|
| 436 |
'external_data_injected': bool(external_data)
|
| 437 |
}
|
| 438 |
}
|
| 439 |
+
|
| 440 |
# Step 3: Coordinate with HF endpoint
|
| 441 |
yield {
|
| 442 |
'type': 'coordination_status',
|
| 443 |
'content': 'π€ Engaging HF endpoint for deep analysis...',
|
| 444 |
'details': {'phase': 'hf_coordination'}
|
| 445 |
}
|
| 446 |
+
|
| 447 |
# Check HF availability
|
| 448 |
hf_available = self._check_hf_availability()
|
| 449 |
if hf_available:
|
|
|
|
| 453 |
'ollama_response_length': len(ollama_response),
|
| 454 |
'external_data_items': len(external_data) if external_data else 0
|
| 455 |
}
|
|
|
|
| 456 |
yield {
|
| 457 |
'type': 'coordination_status',
|
| 458 |
'content': f'π HF context: {len(conversation_history)} conversation turns, Ollama response ({len(ollama_response)} chars)',
|
| 459 |
'details': context_summary
|
| 460 |
}
|
| 461 |
+
|
| 462 |
# Coordinate with HF
|
| 463 |
async for hf_chunk in self._coordinate_hierarchical_hf_response(
|
| 464 |
+
user_id, user_query, conversation_history, external_data, ollama_response
|
|
|
|
| 465 |
):
|
| 466 |
yield hf_chunk
|
| 467 |
else:
|
|
|
|
| 470 |
'content': 'βΉοΈ HF endpoint not available - using Ollama response',
|
| 471 |
'details': {'hf_available': False}
|
| 472 |
}
|
| 473 |
+
|
| 474 |
# Final coordination status
|
| 475 |
yield {
|
| 476 |
'type': 'coordination_status',
|
| 477 |
'content': 'β
Hierarchical coordination complete',
|
| 478 |
'details': {'status': 'complete'}
|
| 479 |
}
|
| 480 |
+
|
| 481 |
except Exception as e:
|
| 482 |
logger.error(f"Hierarchical coordination failed: {e}")
|
| 483 |
yield {
|
|
|
|
| 485 |
'content': f'β Coordination error: {str(e)}',
|
| 486 |
'details': {'error': str(e)}
|
| 487 |
}
|
| 488 |
+
|
| 489 |
+
async def _coordinate_hierarchical_hf_response(self, user_id: str, query: str, history: List, external_data: Dict, ollama_response: str) -> AsyncGenerator[Dict, None]:
|
|
|
|
|
|
|
| 490 |
"""Coordinate with HF endpoint as authoritative layer with streaming"""
|
| 491 |
try:
|
| 492 |
# Check and warm up HF endpoint if needed
|
| 493 |
hf_status = hf_monitor.check_endpoint_status()
|
|
|
|
| 494 |
if not hf_status['available']:
|
| 495 |
yield {'type': 'coordination_status', 'content': 'β‘ Initializing HF endpoint (2-4 minutes)...'}
|
| 496 |
warmup_success = hf_monitor.handle_scale_to_zero()
|
| 497 |
if not warmup_success:
|
| 498 |
yield {'type': 'coordination_status', 'content': 'β HF endpoint initialization failed'}
|
| 499 |
return
|
| 500 |
+
|
| 501 |
# Get HF provider
|
| 502 |
hf_provider = llm_factory.get_provider('huggingface')
|
| 503 |
if not hf_provider:
|
| 504 |
yield {'type': 'coordination_status', 'content': 'β HF provider not available'}
|
| 505 |
return
|
| 506 |
+
|
| 507 |
# Prepare enhanced conversation for HF with hierarchical context
|
| 508 |
enhanced_history = history.copy()
|
| 509 |
+
|
| 510 |
# Inject current time into HF context too
|
| 511 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 512 |
time_context = {
|
|
|
|
| 514 |
"content": f"[Current Date & Time: {current_time}]"
|
| 515 |
}
|
| 516 |
enhanced_history = [time_context] + enhanced_history
|
| 517 |
+
|
| 518 |
# Add system instructions for HF
|
| 519 |
enhanced_history.insert(0, {
|
| 520 |
"role": "system",
|
| 521 |
"content": self.system_instructions['hf_role']
|
| 522 |
})
|
| 523 |
+
|
| 524 |
# Add context about the coordination
|
| 525 |
enhanced_history.append({
|
| 526 |
"role": "system",
|
| 527 |
+
"content": f"""
|
| 528 |
+
Context for your authoritative analysis:
|
| 529 |
+
User Query: '{query}'
|
| 530 |
+
Ollama's Initial Response: '{ollama_response}'
|
| 531 |
+
External Data Available: {json.dumps(external_data, indent=2) if external_data else 'None'}
|
| 532 |
+
Please provide your deep, uncensored analysis that supersedes the local model's response.
|
| 533 |
+
Use your full 8192 token capacity for comprehensive thinking.
|
| 534 |
+
Stream your response for real-time delivery."""
|
| 535 |
})
|
| 536 |
+
|
| 537 |
# Add the user's latest query
|
| 538 |
enhanced_history.append({"role": "user", "content": query})
|
| 539 |
+
|
| 540 |
# Stream HF response with full 8192 token capacity
|
| 541 |
yield {'type': 'coordination_status', 'content': 'π§ HF endpoint thinking...'}
|
| 542 |
+
|
| 543 |
# Use streaming for real-time delivery
|
| 544 |
hf_response_stream = hf_provider.stream_generate(query, enhanced_history)
|
| 545 |
+
|
| 546 |
if hf_response_stream:
|
| 547 |
# Stream the response chunks
|
| 548 |
full_hf_response = ""
|
|
|
|
| 550 |
if chunk:
|
| 551 |
full_hf_response += chunk
|
| 552 |
yield {'type': 'hf_thinking', 'content': chunk}
|
| 553 |
+
|
| 554 |
# Final HF response
|
| 555 |
yield {'type': 'final_response', 'content': full_hf_response}
|
| 556 |
yield {'type': 'coordination_status', 'content': 'π― HF analysis complete and authoritative'}
|
| 557 |
else:
|
| 558 |
yield {'type': 'coordination_status', 'content': 'β HF response generation failed'}
|
| 559 |
+
|
| 560 |
except Exception as e:
|
| 561 |
logger.error(f"Hierarchical HF coordination failed: {e}")
|
| 562 |
yield {'type': 'coordination_status', 'content': f'β HF coordination error: {str(e)}'}
|
| 563 |
+
|
| 564 |
async def _get_hierarchical_ollama_response(self, query: str, history: List, external_data: Dict) -> str:
|
| 565 |
"""Get Ollama response with hierarchical awareness"""
|
| 566 |
try:
|
|
|
|
| 568 |
ollama_provider = llm_factory.get_provider('ollama')
|
| 569 |
if not ollama_provider:
|
| 570 |
raise Exception("Ollama provider not available")
|
| 571 |
+
|
| 572 |
# Prepare conversation with hierarchical context
|
| 573 |
enhanced_history = history.copy()
|
| 574 |
+
|
| 575 |
# Inject current time into Ollama context too
|
| 576 |
current_time = datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
|
| 577 |
time_context = {
|
|
|
|
| 579 |
"content": f"[Current Date & Time: {current_time}]"
|
| 580 |
}
|
| 581 |
enhanced_history = [time_context] + enhanced_history
|
| 582 |
+
|
| 583 |
# Add system instruction for Ollama's role
|
| 584 |
enhanced_history.insert(0, {
|
| 585 |
"role": "system",
|
| 586 |
"content": self.system_instructions['ollama_role']
|
| 587 |
})
|
| 588 |
+
|
| 589 |
# Add external data context if available
|
| 590 |
if external_data:
|
| 591 |
context_parts = []
|
|
|
|
| 596 |
context_parts.append(f"Current weather: {weather.get('temperature', 'N/A')}Β°C in {weather.get('city', 'Unknown')}")
|
| 597 |
if 'current_datetime' in external_data:
|
| 598 |
context_parts.append(f"Current time: {external_data['current_datetime']}")
|
| 599 |
+
|
| 600 |
if context_parts:
|
| 601 |
context_message = {
|
| 602 |
"role": "system",
|
| 603 |
"content": "Context: " + " | ".join(context_parts)
|
| 604 |
}
|
| 605 |
enhanced_history.insert(1, context_message) # Insert after role instruction
|
| 606 |
+
|
| 607 |
# Add the user's query
|
| 608 |
enhanced_history.append({"role": "user", "content": query})
|
| 609 |
+
|
| 610 |
# Generate response with awareness of HF's superior capabilities
|
| 611 |
response = ollama_provider.generate(query, enhanced_history)
|
| 612 |
+
|
| 613 |
# Add acknowledgment of HF's authority
|
| 614 |
if response:
|
| 615 |
return f"{response}\n\n*Note: A more comprehensive analysis from the uncensored HF model is being prepared...*"
|
| 616 |
else:
|
| 617 |
return "I'm processing your request... A deeper analysis is being prepared by the authoritative model."
|
| 618 |
+
|
| 619 |
except Exception as e:
|
| 620 |
logger.error(f"Hierarchical Ollama response failed: {e}")
|
| 621 |
return "I'm thinking about your question... Preparing a comprehensive response."
|
| 622 |
+
|
| 623 |
def _check_hf_availability(self) -> bool:
|
| 624 |
"""Check if HF endpoint is configured and available"""
|
| 625 |
try:
|
|
|
|
| 627 |
return bool(config.hf_token and config.hf_api_url)
|
| 628 |
except:
|
| 629 |
return False
|
| 630 |
+
|
| 631 |
async def _gather_external_data(self, query: str) -> Dict:
|
| 632 |
"""Gather external data from various sources"""
|
| 633 |
data = {}
|
| 634 |
+
|
| 635 |
# Tavily/DuckDuckGo search with justification focus
|
| 636 |
if self.tavily_client or web_search_service.client:
|
| 637 |
try:
|
|
|
|
| 642 |
# data['search_answer'] = ...
|
| 643 |
except Exception as e:
|
| 644 |
logger.warning(f"Tavily search failed: {e}")
|
| 645 |
+
|
| 646 |
# Weather data
|
| 647 |
weather_keywords = ['weather', 'temperature', 'forecast', 'climate', 'rain', 'sunny']
|
| 648 |
if any(keyword in query.lower() for keyword in weather_keywords):
|
|
|
|
| 653 |
data['weather'] = weather
|
| 654 |
except Exception as e:
|
| 655 |
logger.warning(f"Weather data failed: {e}")
|
| 656 |
+
|
| 657 |
# Current date/time
|
| 658 |
data['current_datetime'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 659 |
+
|
| 660 |
return data
|
| 661 |
+
|
| 662 |
def _extract_location(self, query: str) -> Optional[str]:
|
| 663 |
"""Extract location from query"""
|
| 664 |
+
locations = ['New York', 'London', 'Tokyo', 'Paris', 'Berlin', 'Sydney',
|
| 665 |
+
'Los Angeles', 'Chicago', 'Miami', 'Seattle', 'Boston',
|
| 666 |
+
'San Francisco', 'Toronto', 'Vancouver', 'Montreal']
|
| 667 |
+
|
| 668 |
for loc in locations:
|
| 669 |
if loc.lower() in query.lower():
|
| 670 |
return loc
|
| 671 |
return "New York" # Default
|
| 672 |
+
|
| 673 |
def get_coordination_status(self) -> Dict:
|
| 674 |
"""Get current coordination system status"""
|
| 675 |
return {
|
|
|
|
| 682 |
os.getenv("NASA_API_KEY")
|
| 683 |
])
|
| 684 |
}
|
| 685 |
+
|
| 686 |
def get_recent_activities(self, user_id: str) -> Dict:
|
| 687 |
"""Get recent coordination activities for user"""
|
| 688 |
try:
|
core/personality.py
CHANGED
|
@@ -31,7 +31,7 @@ class CosmicCatPersonality:
|
|
| 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
|
|
@@ -44,40 +44,40 @@ class CosmicCatPersonality:
|
|
| 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
|
|
|
|
| 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
|
|
|
|
| 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
|