Spaces:
Sleeping
Sleeping
File size: 18,049 Bytes
e22a015 a1b3907 e22a015 7ed5c62 a1b3907 7ed5c62 e22a015 7ed5c62 e22a015 b480020 87f7faf e22a015 b480020 e22a015 b480020 59307f6 b480020 e22a015 b480020 87f7faf 59307f6 f2eb88d 59307f6 87f7faf 59307f6 87f7faf 59307f6 b480020 59307f6 b480020 59307f6 87f7faf e22a015 7ed5c62 6f76ac1 7ed5c62 e22a015 6f76ac1 e22a015 6f76ac1 e22a015 6f76ac1 e22a015 7ed5c62 e22a015 6f76ac1 e22a015 6f76ac1 e22a015 6f76ac1 e22a015 6f76ac1 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 c31642a 7ed5c62 e22a015 7ed5c62 c31642a e22a015 7ed5c62 e22a015 c31642a e22a015 c31642a e22a015 c31642a e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 a1b3907 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 61964af 7ed5c62 e22a015 6f76ac1 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 7ed5c62 e22a015 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
import datetime
import json
import os
import time
import re
from langchain_core.messages import SystemMessage, HumanMessage
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.tools.retriever import create_retriever_tool
from langgraph.graph import StateGraph, START
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph.state import CompiledStateGraph
from langgraph.graph import MessagesState
from langgraph.prebuilt import ToolNode
from langgraph.prebuilt import tools_condition
from dotenv import load_dotenv
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain_community.tools.tavily_search import TavilySearchResults
from langgraph.graph import END, StateGraph
from nasapy import Nasa
from pydantic import BaseModel
import requests
import streamlit as st
import tavily
# Load environment variables
load_dotenv()
# Initialize models and APIs
llm = ChatGoogleGenerativeAI(
model="gemini-1.5-flash",
google_api_key=os.getenv("GOOGLE_API_KEY")
)
# Initialize models and APIs
llm = ChatGoogleGenerativeAI(
model="gemini-1.5-flash",
google_api_key=os.getenv("GOOGLE_API_KEY")
)
def space_events_agent():
"""
Fetches and formats upcoming space events from NASA's EONET API.
Returns:
dict: A dictionary with the formatted space events or a message if no events are found.
Raises:
HTTPError: If the request to the NASA API fails.
Note:
Requires NASA API key set in the environment variable NASA_API_KEY.
"""
import os
import requests
base_url = "https://eonet.gsfc.nasa.gov/api/v2.1/events"
params = {"api_key": os.getenv("NASA_API_KEY")}
try:
response = requests.get(base_url, params=params)
response.raise_for_status()
except Exception as e:
return {"output": f"🚀 **Space Events Agent:** Error fetching events: {str(e)}"}
events = response.json().get("events", [])
if events:
event_details = []
for event in events:
title = event.get('title', 'N/A')
# Retrieve category using safe defaults
category = (event.get('categories', [{}])[0]).get('title', 'Unknown Category')
geometries = event.get('geometries', [])
if geometries:
first_geometry = geometries[0]
coordinates = first_geometry.get('coordinates', 'Unknown Location')
event_date = first_geometry.get('date', 'Unknown Date')
else:
coordinates = 'Unknown Location'
event_date = 'Unknown Date'
detail = (
f"**Event:** {title}\n"
f"**Category:** {category}\n"
f"**Location:** {coordinates}\n"
f"**Date:** {event_date}\n"
)
event_details.append(detail)
formatted_events = "\n".join(event_details)
return {"output": f"🚀 **Space Events Agent:**\n\n{formatted_events}"}
else:
return {"output": "🚀 **Space Events Agent:** No events found at the moment."}
def astronomy_image_agent(user_input: str):
"""
Retrieves astronomy-related images from NASA's Image and Video Library or the Astronomy Picture of the Day (APOD) API.
If the user query contains specific keywords (e.g., "galaxy", "jupiter", "mars", etc.), it fetches images
from NASA's Image and Video Library using that keyword. If no specific keyword is found, it defaults to fetching
the Astronomy Picture of the Day (APOD).
Args:
user_input (str): The user input containing the query.
Returns:
dict: A dictionary containing details about the requested astronomy image, including the title,
description, and URL. If an error occurs, an error message is returned.
"""
user_input_lower = user_input.lower()
# List of keywords that trigger a search on NASA's Image and Video Library
search_keywords = ["galaxy", "jupiter", "mars", "nebula", "saturn", "comet", "asteroid", "moon"]
found_keyword = None
for keyword in search_keywords:
if keyword in user_input_lower:
found_keyword = keyword
break
if found_keyword:
# Query NASA's Image and Video Library API using the found keyword.
search_url = "https://images-api.nasa.gov/search"
params = {"q": found_keyword, "media_type": "image"}
try:
response = requests.get(search_url, params=params)
response.raise_for_status()
data = response.json()
items = data.get("collection", {}).get("items", [])
if items:
first_item = items[0]
links = first_item.get("links", [])
image_url = links[0]["href"] if links else "No URL available"
title = first_item.get("data", [{}])[0].get("title", "No Title")
description = first_item.get("data", [{}])[0].get("description", "No Description")
result = (
f"Title: {title}\n"
f"Description: {description}\n"
f"Image URL: {image_url}"
)
return {"output": f"✨ {found_keyword.capitalize()} Image Agent:\n\n{result}"}
else:
return {"output": f"No images found for {found_keyword}."}
except Exception as e:
return {"output": f"{found_keyword.capitalize()} image search failed: {str(e)}"}
else:
# Default to the APOD API if no specific keyword is detected.
url = f"https://api.nasa.gov/planetary/apod?api_key={os.getenv('NASA_API_KEY')}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
result = (
f"Title: {data.get('title', 'N/A')}\n"
f"Date: {data.get('date', 'N/A')}\n"
f"Explanation: {data.get('explanation', 'N/A')}\n"
f"Image URL: {data.get('url', 'N/A')}\n"
f"Media Type: {data.get('media_type', 'N/A')}\n"
f"Copyright: {data.get('copyright', 'Public Domain')}"
)
return {"output": f"✨ Astronomy Image Agent:\n\n{result}"}
except Exception as e:
return {"output": f"Astronomy Image Agent Error: {str(e)}"}
def educational_resources_agent():
"""
Retrieves and formats a list of high-quality educational resources on astronomy and space science.
Uses Tavily search to find trusted sources such as NASA, ESA, and other reputable platforms.
Returns:
dict: A dictionary containing an informative summary and a list of educational resources with titles, descriptions, and clickable links.
Raises:
Exception: If an error occurs while retrieving the resources or formatting the response.
"""
try:
# Use Tavily search to retrieve educational resources based on the query
resources = search.invoke({
"query": "Provide a curated list of high-quality educational resources on astronomy and space science, from trusted sources like NASA, ESA, and reputable educational platforms."
})
if not resources:
return {"output": "No educational resources found. Please try again."}
# Format each resource as a clickable link along with a description
formatted_resources = "\n\n".join(
[f"📘 **{r.get('title', 'Untitled Resource')}**\n🔗 [Visit Resource]({r.get('url', 'No URL provided')})" for r in resources]
)
# Add an informative introduction generated by LLM or manually written text
informative_text = (
"Based on your query, here is a curated list of high-quality educational resources on astronomy and space science. "
"These resources have been carefully selected from trusted institutions such as NASA, ESA, and reputable academic platforms. "
"They provide a wealth of knowledge on various space topics, including recent discoveries, research articles, and interactive educational materials."
)
full_response = f"🎓 **Educational Resources Agent:**\n\n{informative_text}\n\n{formatted_resources}"
return {"output": full_response}
except Exception as e:
return {"output": f"Educational Resources Agent Error: {str(e)}"}
def iss_location_agent():
"""
Fetches astronaut data and the current position of the International Space Station (ISS).
It fetches the list of astronauts currently on the ISS, and retrieves the current position of the ISS.
The data is updated at intervals and returned in a structured format.
Returns:
dict: A dictionary containing astronaut information and ISS position updates.
Includes a list of people on the ISS and the positions over time.
"""
# Fetch astronaut data
api_url = "http://api.open-notify.org/astros.json"
data = requests.get(api_url).json()
result = {}
if data['message'] == 'success':
with open('iss-astros.json', 'w') as iss_file:
json.dump(data, iss_file, indent=4)
result["astronauts"] = f"There are currently {data['number']} people on the ISS."
result["people"] = []
for person in data['people']:
result["people"].append(f"{person['name']} is currently on the ISS, Craft: {person['craft']}")
else:
result["error"] = 'Failed to obtain astronauts data.'
# List to hold ISS position updates
positions = []
# fetching ISS position data
for attempt in range(5): # Retry 5 times
try:
data = requests.get("http://api.open-notify.org/iss-now.json", timeout=10).json()
if data['message'] == 'success':
# Extract ISS location
location = data["iss_position"]
latitude = float(location['latitude'])
longitude = float(location['longitude'])
position = {
"latitude": latitude,
"longitude": longitude,
"time": str(datetime.datetime.fromtimestamp(data['timestamp']))
}
positions.append(position)
break # Successfully fetched data, break the loop
except requests.exceptions.RequestException as e:
result["error"] = str(e)
time.sleep(3) # Wait for 3 seconds before retrying
result["positions"] = positions if positions else "Failed to retrieve ISS position data after retries."
return result
search = TavilySearchResults(tavily_api_key=os.getenv("TAVILY_API_KEY"))
tools = [search, iss_location_agent, space_events_agent, astronomy_image_agent, educational_resources_agent]
llm_with_tools = llm.bind_tools(tools)
sys_msg = SystemMessage(content='''This system is designed to provide real-time astronomical data, visualizations, and educational content. Below are the key functions and tools integrated into the system and their specific purposes:
1. **Tavily Search (`search`) Integration**:
- **Purpose**: Provides users with up-to-date, relevant space-related information from Tavily's extensive search engine.
- **Usage**: Enables the assistant to fetch the latest news, research articles, and educational resources related to space.
2. **NASA APOD Tool (`get_nasa_apod_tool`)**:
- **Purpose**: Fetches the Astronomy Picture of the Day (APOD) from NASA's APOD API.
- **Usage**: Provides the title, explanation, and image URL of the latest astronomy image shared by NASA, offering users daily insights into the wonders of space.
3. **NASA EONET Space Events (`fetch_nasa_eonet_events`)**:
- **Purpose**: Retrieves real-time space-related events from NASA’s EONET API.
- **Usage**: Provides details about ongoing or upcoming space events, including event type, title, location, and date, offering users information about significant celestial events.
4. **ISS Location Plotting (`plot_iss_location`)**:
- **Purpose**: Displays the current real-time location of the International Space Station (ISS).
- **Usage**: Visualizes the ISS position on a latitude-longitude graph, allowing users to track its orbit across the globe in real-time.
5. **ISS Astronaut Data (`ISS_data`)**:
- **Purpose**: Provides data about astronauts currently aboard the ISS and their associated spacecraft.
- **Usage**: Retrieves information on the number of astronauts aboard, their spacecraft, and visualizes the ISS’s position on a world map.
6. **Space Educational Fact (`educational_space_fact`)**:
- **Purpose**: Fetches random educational facts about space from the Space Trivia API.
- **Usage**: Provides fascinating trivia about celestial bodies, space phenomena, and other space-related topics, helping to educate users in an engaging manner.
7. **NEO Data Fetcher (`get_neo_data`)**:
- **Purpose**: Retrieves real-time data on Near-Earth Objects (NEOs) from NASA’s NEO API.
- **Usage**: Provides information on potentially hazardous asteroids and comets, helping users stay informed about objects that come close to Earth.
8. **Space News Agent (`retriever_tool`)**:
- **Purpose**: Fetches the latest space news articles.
- **Usage**: Provides users with up-to-date news articles related to space exploration, satellite launches, and astronomical discoveries.
### Workflow:
- The system responds dynamically to user queries by identifying the appropriate tool for each request.
- If the user asks for data or visualizations related to astronomical objects or space events, the relevant NASA APIs or visualization tools are invoked.
- For general space education or research, tools like Tavily and Space Trivia provide comprehensive, up-to-date educational content.
- The system remembers the context of conversations to ensure relevant and seamless interactions with users, supported by the `MemorySaver`.
### Guidelines:
- **Scientific Accuracy**: All provided information is scientifically accurate and up-to-date.
- **Engagement**: Educational content is easy to understand and presented in an interactive and engaging way.
- **Visualization**: For requests requiring visual representations (such as ISS tracking or NEO data), interactive orbital plots or maps are rendered for clarity.
- **Tool Alignment**: The system uses the most appropriate tool based on the user’s request, ensuring minimal invocation of unnecessary functions.
This setup ensures that the assistant can efficiently address a wide range of user queries related to space, astronomy, and related educational content while keeping interactions intuitive and engaging.
''')
# Node
def assistant(state: MessagesState) -> MessagesState:
return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"][-10:])]}
# Build graph
builder: StateGraph = StateGraph(MessagesState)
# Define nodes: these do the work
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
# Define edges: these determine how the control flow moves
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
"assistant",
# If the latest message (result) from assistant is a tool call -> tools_condition routes to tools
# If the latest message (result) from assistant is not a tool call -> tools_condition routes to END
tools_condition,
)
builder.add_edge("tools", "assistant")
memory: MemorySaver = MemorySaver()
# Compile the workflow into an application
react_graph_memory: CompiledStateGraph = builder.compile(checkpointer=memory)
st.title("🌠CosmoBot🌠: Unveiling the mysteries of space")
st.caption("Your AI-powered space exploration assistant")
if "messages" not in st.session_state:
st.session_state.messages = []
# Display existing chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
if message["content"].startswith("http"):
st.image(message["content"])
else:
st.write(message["content"])
# User input via chat
if user_input := st.chat_input("Ask about space news, ISS location, Educational resource or astronomy images"):
st.session_state.messages.append({"role": "user", "content": user_input})
st.chat_message("user").write(user_input)
try:
# Prepare the messages for the model
messages = [HumanMessage(content=user_input)]
# Call the memory and invoke the response
response = react_graph_memory.invoke({"messages": messages}, config={"configurable": {"thread_id": "1"}})
assistant_response = response["messages"][-1].content
# Use regex to detect image URLs in the assistant response
image_pattern = re.compile(r'(https?://\S+\.(?:png|jpg|jpeg|gif))', re.IGNORECASE)
match = image_pattern.search(assistant_response)
if match:
image_url = match.group(1)
st.session_state.messages.append({"role": "assistant", "content": image_url})
st.session_state.messages.append({"role": "assistant", "content": assistant_response})
with st.chat_message("assistant"):
st.image(image_url)
st.write(assistant_response)
else:
st.session_state.messages.append({"role": "assistant", "content": assistant_response})
with st.chat_message("assistant"):
st.write(assistant_response)
except Exception as e:
error_msg = f"Error processing request: {str(e)}"
st.session_state.messages.append({"role": "assistant", "content": error_msg})
with st.chat_message("assistant"):
st.write(error_msg)
|