CCockrum commited on
Commit
b256ef1
Β·
verified Β·
1 Parent(s): b909d57

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -80
app.py CHANGED
@@ -1,24 +1,27 @@
1
  import os
2
  import re
3
  import random
 
4
  import requests
5
  import streamlit as st
 
6
  from langchain_huggingface import HuggingFaceEndpoint
7
  from langchain_core.prompts import PromptTemplate
8
  from langchain_core.output_parsers import StrOutputParser
9
  from transformers import pipeline
10
 
11
- # Use environment variables for keys
12
- HF_TOKEN = os.getenv("HF_TOKEN")
13
- if HF_TOKEN is None:
14
- raise ValueError("HF_TOKEN environment variable not set. Please set it in your Hugging Face Space settings.")
15
 
16
- NASA_API_KEY = os.getenv("NASA_API_KEY")
17
- if NASA_API_KEY is None:
18
- raise ValueError("NASA_API_KEY environment variable not set. Please set it in your Hugging Face Space settings.")
 
 
 
 
19
 
20
- # Set up Streamlit UI
21
- st.set_page_config(page_title="HAL - NASA ChatBot", page_icon="πŸš€")
22
 
23
  # --- Initialize Session State Variables ---
24
  if "chat_history" not in st.session_state:
@@ -28,6 +31,38 @@ if "response_ready" not in st.session_state:
28
  if "follow_up" not in st.session_state:
29
  st.session_state.follow_up = ""
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  # --- Set Up Model & API Functions ---
32
  model_id = "mistralai/Mistral-7B-Instruct-v0.3"
33
  sentiment_analyzer = pipeline(
@@ -41,12 +76,12 @@ def get_llm_hf_inference(model_id=model_id, max_new_tokens=128, temperature=0.7)
41
  repo_id=model_id,
42
  max_new_tokens=max_new_tokens,
43
  temperature=temperature,
44
- token=HF_TOKEN,
45
  task="text-generation"
46
  )
47
 
48
  def get_nasa_apod():
49
- url = f"https://api.nasa.gov/planetary/apod?api_key={NASA_API_KEY}"
50
  response = requests.get(url)
51
  if response.status_code == 200:
52
  data = response.json()
@@ -59,20 +94,35 @@ def analyze_sentiment(user_text):
59
  return result['label']
60
 
61
  def predict_action(user_text):
62
- if "NASA" in user_text or "space" in user_text:
63
  return "nasa_info"
64
  return "general_query"
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  def generate_follow_up(user_text):
67
  """
68
  Generates two variant follow-up questions and randomly selects one.
69
- It also cleans up any unwanted quotation marks or extra meta commentary.
70
  """
71
  prompt_text = (
72
  f"Based on the user's question: '{user_text}', generate two concise, friendly follow-up questions "
73
  "that invite further discussion. For example, one might be 'Would you like to know more about the six types of quarks?' "
74
- "and another might be 'Would you like to explore another aspect of quantum physics?' Do not include extra commentary ."
75
- "Answer exclusively in English, and do not include extra commentary."
76
  )
77
  hf = get_llm_hf_inference(max_new_tokens=80, temperature=0.9)
78
  output = hf.invoke(input=prompt_text).strip()
@@ -82,25 +132,16 @@ def generate_follow_up(user_text):
82
  cleaned = ["Would you like to explore this topic further?"]
83
  return random.choice(cleaned)
84
 
85
- def get_response(system_message, chat_history, user_text, max_new_tokens=512):
86
  """
87
- Generates HAL's answer with depth and a follow-up question.
88
- The prompt instructs the model to provide a detailed explanation and then generate a follow-up.
89
- If the answer comes back empty, a fallback answer is used.
90
  """
91
  sentiment = analyze_sentiment(user_text)
92
  action = predict_action(user_text)
93
 
94
- # Extract style instruction if present
95
- style_instruction = ""
96
- lower_text = user_text.lower()
97
- if "in the voice of" in lower_text or "speaking as" in lower_text:
98
- match = re.search(r"(in the voice of|speaking as)(.*)", lower_text)
99
- if match:
100
- style_instruction = match.group(2).strip().capitalize()
101
- style_instruction = f" Please respond in the voice of {style_instruction}."
102
-
103
- if action == "nasa_info":
104
  nasa_url, nasa_title, nasa_explanation = get_nasa_apod()
105
  response = f"**{nasa_title}**\n\n{nasa_explanation}"
106
  chat_history.append({'role': 'user', 'content': user_text})
@@ -108,7 +149,8 @@ def get_response(system_message, chat_history, user_text, max_new_tokens=512):
108
  follow_up = generate_follow_up(user_text)
109
  chat_history.append({'role': 'assistant', 'content': follow_up})
110
  return response, follow_up, chat_history, nasa_url
111
-
 
112
  hf = get_llm_hf_inference(max_new_tokens=max_new_tokens, temperature=0.9)
113
  filtered_history = ""
114
  for message in chat_history:
@@ -116,27 +158,34 @@ def get_response(system_message, chat_history, user_text, max_new_tokens=512):
116
  continue
117
  filtered_history += f"{message['role']}: {message['content']}\n"
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  style_clause = style_instruction if style_instruction else ""
120
 
121
- # Instruct the model to generate a detailed, in-depth answer.
122
  prompt = PromptTemplate.from_template(
123
  (
124
  "[INST] {system_message}\n\nCurrent Conversation:\n{chat_history}\n\n"
125
  "User: {user_text}.\n [/INST]\n"
126
- "AI: Please provide a detailed explanation in depth. "
127
- "Ensure your response covers the topic thoroughly and is written in a friendly, conversational style, "
128
- "starting with a phrase like 'Certainly!', 'Of course!', or 'Great question!'."
129
- "Answer exclusively in English, and do not include extra commentary."+ style_clause +
130
  "\nHAL:"
131
  )
132
  )
133
 
134
  chat = prompt | hf.bind(skip_prompt=True) | StrOutputParser(output_key='content')
135
- response = chat.invoke(input=dict(system_message=system_message, user_text=user_text, chat_history=filtered_history))
136
- # Remove any extra markers if present.
137
- response = response.split("HAL:")[-1].strip()
138
-
139
- # Fallback in case the generated answer is empty
140
  if not response:
141
  response = "Certainly, here is an in-depth explanation: [Fallback explanation]."
142
 
@@ -156,44 +205,19 @@ def get_response(system_message, chat_history, user_text, max_new_tokens=512):
156
  st.title("πŸš€ HAL - Your NASA AI Assistant")
157
  st.markdown("🌌 *Ask me about space, NASA, and beyond!*")
158
 
159
- #Reset Button
160
  if st.sidebar.button("Reset Chat"):
161
  st.session_state.chat_history = [{"role": "assistant", "content": "Hello! How can I assist you today?"}]
162
  st.session_state.response_ready = False
163
  st.session_state.follow_up = ""
164
  st.experimental_rerun()
165
 
166
- #Style and Appearance
167
- st.markdown("""
168
- <style>
169
- .user-msg {
170
- background-color: #696969;
171
- color: white;
172
- padding: 10px;
173
- border-radius: 10px;
174
- margin-bottom: 5px;
175
- width: fit-content;
176
- max-width: 80%;
177
- }
178
- .assistant-msg {
179
- background-color: #333333;
180
- color: white;
181
- padding: 10px;
182
- border-radius: 10px;
183
- margin-bottom: 5px;
184
- width: fit-content;
185
- max-width: 80%;
186
- }
187
- .container {
188
- display: flex;
189
- flex-direction: column;
190
- align-items: flex-start;
191
- }
192
- @media (max-width: 600px) {
193
- .user-msg, .assistant-msg { font-size: 16px; max-width: 100%; }
194
- }
195
- </style>
196
- """, unsafe_allow_html=True)
197
 
198
  user_input = st.chat_input("Type your message here...")
199
 
@@ -207,11 +231,3 @@ if user_input:
207
  st.image(image_url, caption="NASA Image of the Day")
208
  st.session_state.follow_up = follow_up
209
  st.session_state.response_ready = True
210
-
211
- st.markdown("<div class='container'>", unsafe_allow_html=True)
212
- for message in st.session_state.chat_history:
213
- if message["role"] == "user":
214
- st.markdown(f"<div class='user-msg'><strong>You:</strong> {message['content']}</div>", unsafe_allow_html=True)
215
- else:
216
- st.markdown(f"<div class='assistant-msg'><strong>HAL:</strong> {message['content']}</div>", unsafe_allow_html=True)
217
- st.markdown("</div>", unsafe_allow_html=True)
 
1
  import os
2
  import re
3
  import random
4
+ import subprocess
5
  import requests
6
  import streamlit as st
7
+ import spacy # for additional NLP processing
8
  from langchain_huggingface import HuggingFaceEndpoint
9
  from langchain_core.prompts import PromptTemplate
10
  from langchain_core.output_parsers import StrOutputParser
11
  from transformers import pipeline
12
 
13
+ # Must be the first Streamlit command!
14
+ st.set_page_config(page_title="HAL - NASA ChatBot", page_icon="πŸš€")
 
 
15
 
16
+ # --- Helper to load spaCy model with fallback ---
17
+ def load_spacy_model():
18
+ try:
19
+ return spacy.load("en_core_web_sm")
20
+ except OSError:
21
+ subprocess.run(["python", "-m", "spacy", "download", "en_core_web_sm"], check=True)
22
+ return spacy.load("en_core_web_sm")
23
 
24
+ nlp_spacy = load_spacy_model()
 
25
 
26
  # --- Initialize Session State Variables ---
27
  if "chat_history" not in st.session_state:
 
31
  if "follow_up" not in st.session_state:
32
  st.session_state.follow_up = ""
33
 
34
+ # --- Appearance CSS ---
35
+ st.markdown("""
36
+ <style>
37
+ .user-msg {
38
+ background-color: #696969;
39
+ color: white;
40
+ padding: 10px;
41
+ border-radius: 10px;
42
+ margin-bottom: 5px;
43
+ width: fit-content;
44
+ max-width: 80%;
45
+ }
46
+ .assistant-msg {
47
+ background-color: #333333;
48
+ color: white;
49
+ padding: 10px;
50
+ border-radius: 10px;
51
+ margin-bottom: 5px;
52
+ width: fit-content;
53
+ max-width: 80%;
54
+ }
55
+ .container {
56
+ display: flex;
57
+ flex-direction: column;
58
+ align-items: flex-start;
59
+ }
60
+ @media (max-width: 600px) {
61
+ .user-msg, .assistant-msg { font-size: 16px; max-width: 100%; }
62
+ }
63
+ </style>
64
+ """, unsafe_allow_html=True)
65
+
66
  # --- Set Up Model & API Functions ---
67
  model_id = "mistralai/Mistral-7B-Instruct-v0.3"
68
  sentiment_analyzer = pipeline(
 
76
  repo_id=model_id,
77
  max_new_tokens=max_new_tokens,
78
  temperature=temperature,
79
+ token=os.getenv("HF_TOKEN"),
80
  task="text-generation"
81
  )
82
 
83
  def get_nasa_apod():
84
+ url = f"https://api.nasa.gov/planetary/apod?api_key={os.getenv('NASA_API_KEY')}"
85
  response = requests.get(url)
86
  if response.status_code == 200:
87
  data = response.json()
 
94
  return result['label']
95
 
96
  def predict_action(user_text):
97
+ if "nasa" in user_text.lower() or "space" in user_text.lower():
98
  return "nasa_info"
99
  return "general_query"
100
 
101
+ def extract_context(text):
102
+ """
103
+ Uses spaCy to extract named entities for additional context.
104
+ """
105
+ doc = nlp_spacy(text)
106
+ entities = [ent.text for ent in doc.ents]
107
+ return ", ".join(entities) if entities else ""
108
+
109
+ def is_apod_query(user_text):
110
+ """
111
+ Checks if the user's question contains keywords indicating they are asking for
112
+ the Astronomy Picture of the Day.
113
+ """
114
+ keywords = ["apod", "image", "picture", "photo", "astronomy picture"]
115
+ return any(keyword in user_text.lower() for keyword in keywords)
116
+
117
  def generate_follow_up(user_text):
118
  """
119
  Generates two variant follow-up questions and randomly selects one.
 
120
  """
121
  prompt_text = (
122
  f"Based on the user's question: '{user_text}', generate two concise, friendly follow-up questions "
123
  "that invite further discussion. For example, one might be 'Would you like to know more about the six types of quarks?' "
124
+ "and another 'Would you like to explore another aspect of quantum physics?'. Do not include extra commentary. "
125
+ "Answer exclusively in English."
126
  )
127
  hf = get_llm_hf_inference(max_new_tokens=80, temperature=0.9)
128
  output = hf.invoke(input=prompt_text).strip()
 
132
  cleaned = ["Would you like to explore this topic further?"]
133
  return random.choice(cleaned)
134
 
135
+ def get_response(system_message, chat_history, user_text, max_new_tokens=1024):
136
  """
137
+ Generates HAL's detailed, in-depth answer and a follow-up question.
138
+ Incorporates sentiment analysis, additional NLP context, and style instructions.
 
139
  """
140
  sentiment = analyze_sentiment(user_text)
141
  action = predict_action(user_text)
142
 
143
+ # If the user's NASA-related query is specifically an APOD query, handle it specially.
144
+ if action == "nasa_info" and is_apod_query(user_text):
 
 
 
 
 
 
 
 
145
  nasa_url, nasa_title, nasa_explanation = get_nasa_apod()
146
  response = f"**{nasa_title}**\n\n{nasa_explanation}"
147
  chat_history.append({'role': 'user', 'content': user_text})
 
149
  follow_up = generate_follow_up(user_text)
150
  chat_history.append({'role': 'assistant', 'content': follow_up})
151
  return response, follow_up, chat_history, nasa_url
152
+
153
+ # Otherwise, treat NASA-related queries as general queries.
154
  hf = get_llm_hf_inference(max_new_tokens=max_new_tokens, temperature=0.9)
155
  filtered_history = ""
156
  for message in chat_history:
 
158
  continue
159
  filtered_history += f"{message['role']}: {message['content']}\n"
160
 
161
+ # Extract style instructions if provided.
162
+ style_instruction = ""
163
+ lower_text = user_text.lower()
164
+ if "in the voice of" in lower_text or "speaking as" in lower_text:
165
+ match = re.search(r"(in the voice of|speaking as)(.*)", lower_text)
166
+ if match:
167
+ style_instruction = match.group(2).strip().capitalize()
168
+ style_instruction = f" Please respond in the voice of {style_instruction}."
169
+
170
+ context_info = extract_context(user_text)
171
+ context_clause = f" The key topics here are: {context_info}." if context_info else ""
172
+ language_clause = " Answer exclusively in English."
173
+
174
  style_clause = style_instruction if style_instruction else ""
175
 
 
176
  prompt = PromptTemplate.from_template(
177
  (
178
  "[INST] {system_message}\n\nCurrent Conversation:\n{chat_history}\n\n"
179
  "User: {user_text}.\n [/INST]\n"
180
+ "AI: Please provide a detailed, in-depth answer in a friendly, conversational tone that thoroughly covers the topic."
181
+ + style_clause + context_clause + language_clause +
 
 
182
  "\nHAL:"
183
  )
184
  )
185
 
186
  chat = prompt | hf.bind(skip_prompt=True) | StrOutputParser(output_key='content')
187
+ raw_output = chat.invoke(input=dict(system_message=system_message, user_text=user_text, chat_history=filtered_history))
188
+ response = raw_output.split("HAL:")[-1].strip()
 
 
 
189
  if not response:
190
  response = "Certainly, here is an in-depth explanation: [Fallback explanation]."
191
 
 
205
  st.title("πŸš€ HAL - Your NASA AI Assistant")
206
  st.markdown("🌌 *Ask me about space, NASA, and beyond!*")
207
 
 
208
  if st.sidebar.button("Reset Chat"):
209
  st.session_state.chat_history = [{"role": "assistant", "content": "Hello! How can I assist you today?"}]
210
  st.session_state.response_ready = False
211
  st.session_state.follow_up = ""
212
  st.experimental_rerun()
213
 
214
+ st.markdown("<div class='container'>", unsafe_allow_html=True)
215
+ for message in st.session_state.chat_history:
216
+ if message["role"] == "user":
217
+ st.markdown(f"<div class='user-msg'><strong>You:</strong> {message['content']}</div>", unsafe_allow_html=True)
218
+ else:
219
+ st.markdown(f"<div class='assistant-msg'><strong>HAL:</strong> {message['content']}</div>", unsafe_allow_html=True)
220
+ st.markdown("</div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
  user_input = st.chat_input("Type your message here...")
223
 
 
231
  st.image(image_url, caption="NASA Image of the Day")
232
  st.session_state.follow_up = follow_up
233
  st.session_state.response_ready = True