Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
vprzybylo
commited on
Commit
·
5135378
1
Parent(s):
c54d943
Remove app.py and create an empty __init__.py file in the app directory
Browse files- app.py +270 -5
- app/__init__.py +0 -0
- app/src/ui/app.py +0 -279
app.py
CHANGED
@@ -1,11 +1,276 @@
|
|
|
|
|
|
1 |
import sys
|
2 |
from pathlib import Path
|
|
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
from ui.app import main
|
9 |
|
10 |
if __name__ == "__main__":
|
11 |
-
main()
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
import sys
|
4 |
from pathlib import Path
|
5 |
+
from typing import Annotated, TypedDict
|
6 |
|
7 |
+
import requests
|
8 |
+
import streamlit as st
|
9 |
+
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
10 |
+
from langchain_core.prompts import (
|
11 |
+
ChatPromptTemplate,
|
12 |
+
HumanMessagePromptTemplate,
|
13 |
+
MessagesPlaceholder,
|
14 |
+
SystemMessagePromptTemplate,
|
15 |
+
)
|
16 |
+
from langchain_core.tools import Tool
|
17 |
+
from langchain_openai import ChatOpenAI
|
18 |
+
from langgraph.graph.message import add_messages
|
19 |
+
|
20 |
+
sys.path.append(str(Path(__file__).parent))
|
21 |
+
|
22 |
+
from dotenv import load_dotenv
|
23 |
+
|
24 |
+
# Now import from app.src
|
25 |
+
from app.src.embedding.model import EmbeddingModel
|
26 |
+
from app.src.rag.chain import RAGChain
|
27 |
+
from app.src.rag.document_loader import GridCodeLoader
|
28 |
+
from app.src.rag.vectorstore import VectorStore
|
29 |
+
|
30 |
+
# Load .env file from base directory
|
31 |
+
load_dotenv(Path(__file__).parent / ".env")
|
32 |
+
logger = logging.getLogger(__name__)
|
33 |
+
|
34 |
+
|
35 |
+
def get_secrets():
|
36 |
+
"""Get secrets from environment variables."""
|
37 |
+
# Skip trying Streamlit secrets and go straight to environment variables
|
38 |
+
return {
|
39 |
+
"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY"),
|
40 |
+
"LANGCHAIN_API_KEY": os.getenv("LANGCHAIN_API_KEY"),
|
41 |
+
"LANGCHAIN_PROJECT": os.getenv("LANGCHAIN_PROJECT", "GridGuide"),
|
42 |
+
"LANGCHAIN_TRACING_V2": os.getenv("LANGCHAIN_TRACING_V2", "true"),
|
43 |
+
}
|
44 |
+
|
45 |
+
|
46 |
+
# Set up environment variables from secrets
|
47 |
+
secrets = get_secrets()
|
48 |
+
for key, value in secrets.items():
|
49 |
+
if value:
|
50 |
+
os.environ[key] = value
|
51 |
+
|
52 |
+
# Verify API keys without showing warning
|
53 |
+
if not os.getenv("OPENAI_API_KEY"):
|
54 |
+
st.error("OpenAI API key not found. Please check your .env file.")
|
55 |
+
st.stop()
|
56 |
+
|
57 |
+
|
58 |
+
class WeatherTool:
|
59 |
+
def __init__(self):
|
60 |
+
self.base_url = "https://api.weather.gov"
|
61 |
+
self.headers = {
|
62 |
+
"User-Agent": "(Grid Code Assistant, [email protected])",
|
63 |
+
"Accept": "application/json",
|
64 |
+
}
|
65 |
+
|
66 |
+
def get_coordinates_from_zip(self, zipcode):
|
67 |
+
response = requests.get(f"https://api.zippopotam.us/us/{zipcode}")
|
68 |
+
if response.status_code == 200:
|
69 |
+
data = response.json()
|
70 |
+
return {
|
71 |
+
"lat": data["places"][0]["latitude"],
|
72 |
+
"lon": data["places"][0]["longitude"],
|
73 |
+
"place": data["places"][0]["place name"],
|
74 |
+
"state": data["places"][0]["state"],
|
75 |
+
}
|
76 |
+
return None
|
77 |
+
|
78 |
+
def run(self, zipcode):
|
79 |
+
coords = self.get_coordinates_from_zip(zipcode)
|
80 |
+
if not coords:
|
81 |
+
return {"error": "Invalid ZIP code or unable to get coordinates."}
|
82 |
+
|
83 |
+
point_url = f"{self.base_url}/points/{coords['lat']},{coords['lon']}"
|
84 |
+
response = requests.get(point_url, headers=self.headers)
|
85 |
+
|
86 |
+
if response.status_code != 200:
|
87 |
+
return {"error": "Unable to fetch weather data."}
|
88 |
+
|
89 |
+
grid_data = response.json()
|
90 |
+
forecast_url = grid_data["properties"]["forecast"]
|
91 |
+
|
92 |
+
response = requests.get(forecast_url, headers=self.headers)
|
93 |
+
if response.status_code == 200:
|
94 |
+
forecast_data = response.json()["properties"]["periods"]
|
95 |
+
weather_data = {
|
96 |
+
"type": "weather",
|
97 |
+
"location": f"{coords['place']}, {coords['state']}",
|
98 |
+
"current": forecast_data[0],
|
99 |
+
"forecast": forecast_data[1:4],
|
100 |
+
}
|
101 |
+
# Save to session state
|
102 |
+
st.session_state.weather_data = weather_data
|
103 |
+
return weather_data
|
104 |
+
return {"error": "Unable to fetch forecast data."}
|
105 |
+
|
106 |
+
|
107 |
+
def initialize_rag():
|
108 |
+
"""Initialize RAG system."""
|
109 |
+
if "rag_chain" in st.session_state:
|
110 |
+
logger.info("Using cached RAG chain from session state")
|
111 |
+
return st.session_state.rag_chain
|
112 |
+
|
113 |
+
data_path = "app/data/raw/grid_code.pdf"
|
114 |
+
if not os.path.exists(data_path):
|
115 |
+
raise FileNotFoundError(f"PDF not found: {data_path}")
|
116 |
+
|
117 |
+
with st.spinner("Loading Grid Code documents..."):
|
118 |
+
loader = GridCodeLoader(str(data_path), pages=17)
|
119 |
+
documents = loader.load_and_split()
|
120 |
+
logger.info(f"Loaded {len(documents)} document chunks")
|
121 |
+
|
122 |
+
with st.spinner("Creating vector store..."):
|
123 |
+
embedding_model = EmbeddingModel()
|
124 |
+
vectorstore = VectorStore(embedding_model)
|
125 |
+
vectorstore = vectorstore.create_vectorstore(documents)
|
126 |
+
logger.info("Vector store created successfully")
|
127 |
+
|
128 |
+
# Cache the RAG chain in session state
|
129 |
+
rag_chain = RAGChain(vectorstore)
|
130 |
+
st.session_state.rag_chain = rag_chain
|
131 |
+
return rag_chain
|
132 |
+
|
133 |
+
|
134 |
+
class RAGTool:
|
135 |
+
def __init__(self, rag_chain):
|
136 |
+
self.rag_chain = rag_chain
|
137 |
+
|
138 |
+
def run(self, question: str) -> str:
|
139 |
+
"""Answer questions using the Grid Code."""
|
140 |
+
response = self.rag_chain.invoke(question)
|
141 |
+
return response["answer"]
|
142 |
+
|
143 |
+
|
144 |
+
class AgentState(TypedDict):
|
145 |
+
"""State definition for the agent."""
|
146 |
+
|
147 |
+
messages: Annotated[list, add_messages]
|
148 |
+
|
149 |
+
|
150 |
+
def create_agent_workflow(rag_chain, weather_tool):
|
151 |
+
"""Create an agent that can use both RAG and weather tools."""
|
152 |
+
|
153 |
+
# Define the tools
|
154 |
+
tools = [
|
155 |
+
Tool(
|
156 |
+
name="grid_code_query",
|
157 |
+
description="Answer questions about the Grid Code and electrical regulations",
|
158 |
+
func=lambda q: rag_chain.invoke(q)["answer"],
|
159 |
+
),
|
160 |
+
Tool(
|
161 |
+
name="get_weather",
|
162 |
+
description="Get weather forecast for a ZIP code. Input should be a 5-digit ZIP code.",
|
163 |
+
func=lambda z: weather_tool.run(z),
|
164 |
+
),
|
165 |
+
]
|
166 |
+
|
167 |
+
# Initialize the LLM
|
168 |
+
llm = ChatOpenAI(model="gpt-4o", temperature=0)
|
169 |
+
|
170 |
+
# Create the custom prompt
|
171 |
+
prompt = ChatPromptTemplate.from_messages(
|
172 |
+
[
|
173 |
+
SystemMessagePromptTemplate.from_template(
|
174 |
+
"""You are a helpful assistant that specializes in two areas:
|
175 |
+
1. Answering questions about electrical Grid Code regulations
|
176 |
+
2. Providing weather information for specific locations
|
177 |
+
|
178 |
+
For weather queries:
|
179 |
+
- Extract the ZIP code from the question
|
180 |
+
- Use the get_weather tool to fetch the forecast
|
181 |
+
|
182 |
+
For Grid Code questions:
|
183 |
+
- Use the grid_code_query tool to find relevant information
|
184 |
+
- If the information isn't in the Grid Code, clearly state that
|
185 |
+
- Provide specific references when possible
|
186 |
+
"""
|
187 |
+
),
|
188 |
+
MessagesPlaceholder(variable_name="chat_history", optional=True),
|
189 |
+
HumanMessagePromptTemplate.from_template("{input}"),
|
190 |
+
MessagesPlaceholder(variable_name="agent_scratchpad"),
|
191 |
+
]
|
192 |
+
)
|
193 |
+
|
194 |
+
# Create the agent
|
195 |
+
agent = create_tool_calling_agent(llm, tools, prompt)
|
196 |
+
|
197 |
+
return AgentExecutor(
|
198 |
+
agent=agent,
|
199 |
+
tools=tools,
|
200 |
+
verbose=True,
|
201 |
+
handle_parsing_errors=True,
|
202 |
+
)
|
203 |
+
|
204 |
+
|
205 |
+
def display_weather(weather_data):
|
206 |
+
"""Display weather information in a nice format"""
|
207 |
+
if "error" in weather_data:
|
208 |
+
st.error(weather_data["error"])
|
209 |
+
return
|
210 |
+
|
211 |
+
if weather_data.get("type") == "weather":
|
212 |
+
# Location header
|
213 |
+
st.header(f"Weather for {weather_data['location']}")
|
214 |
+
|
215 |
+
# Current conditions
|
216 |
+
current = weather_data["current"]
|
217 |
+
st.subheader("Current Conditions")
|
218 |
+
|
219 |
+
# Use columns for current weather layout
|
220 |
+
col1, col2 = st.columns(2)
|
221 |
+
|
222 |
+
with col1:
|
223 |
+
# Temperature display with metric
|
224 |
+
st.metric(
|
225 |
+
"Temperature", f"{current['temperature']}°{current['temperatureUnit']}"
|
226 |
+
)
|
227 |
+
# Wind information
|
228 |
+
st.info(f"💨 Wind: {current['windSpeed']} {current['windDirection']}")
|
229 |
+
|
230 |
+
with col2:
|
231 |
+
# Current forecast
|
232 |
+
st.markdown(f"**🌤️ Conditions:** {current['shortForecast']}")
|
233 |
+
st.markdown(f"**📝 Details:** {current['detailedForecast']}")
|
234 |
+
|
235 |
+
# Extended forecast
|
236 |
+
st.subheader("Extended Forecast")
|
237 |
+
for period in weather_data["forecast"]:
|
238 |
+
with st.expander(f"📅 {period['name']}"):
|
239 |
+
st.markdown(
|
240 |
+
f"**🌡️ Temperature:** {period['temperature']}°{period['temperatureUnit']}"
|
241 |
+
)
|
242 |
+
st.markdown(
|
243 |
+
f"**💨 Wind:** {period['windSpeed']} {period['windDirection']}"
|
244 |
+
)
|
245 |
+
st.markdown(f"**🌤️ Forecast:** {period['shortForecast']}")
|
246 |
+
st.markdown(f"**📝 Details:** {period['detailedForecast']}")
|
247 |
+
|
248 |
+
|
249 |
+
def main():
|
250 |
+
st.title("GridGuide: Field Assistant")
|
251 |
+
|
252 |
+
# Initialize if not in session state
|
253 |
+
if "app" not in st.session_state:
|
254 |
+
rag_chain = initialize_rag()
|
255 |
+
weather_tool = WeatherTool()
|
256 |
+
st.session_state.app = create_agent_workflow(rag_chain, weather_tool)
|
257 |
+
|
258 |
+
# Create the input box
|
259 |
+
user_input = st.text_input("Ask about weather or the Grid Code:")
|
260 |
+
|
261 |
+
if user_input:
|
262 |
+
with st.spinner("Processing your request..."):
|
263 |
+
# Invoke the agent executor
|
264 |
+
result = st.session_state.app.invoke({"input": user_input})
|
265 |
+
|
266 |
+
# Check if we have weather data in session state
|
267 |
+
if "weather_data" in st.session_state:
|
268 |
+
display_weather(st.session_state.weather_data)
|
269 |
+
# Clear the weather data after displaying
|
270 |
+
del st.session_state.weather_data
|
271 |
+
else:
|
272 |
+
st.write(result["output"])
|
273 |
|
|
|
274 |
|
275 |
if __name__ == "__main__":
|
276 |
+
main()
|
app/__init__.py
ADDED
File without changes
|
app/src/ui/app.py
DELETED
@@ -1,279 +0,0 @@
|
|
1 |
-
import logging
|
2 |
-
import os
|
3 |
-
import sys
|
4 |
-
from pathlib import Path
|
5 |
-
from typing import Annotated, TypedDict
|
6 |
-
|
7 |
-
import requests
|
8 |
-
import streamlit as st
|
9 |
-
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
10 |
-
from langchain_core.prompts import (
|
11 |
-
ChatPromptTemplate,
|
12 |
-
HumanMessagePromptTemplate,
|
13 |
-
MessagesPlaceholder,
|
14 |
-
SystemMessagePromptTemplate,
|
15 |
-
)
|
16 |
-
from langchain_core.tools import Tool
|
17 |
-
from langchain_openai import ChatOpenAI
|
18 |
-
from langgraph.graph.message import add_messages
|
19 |
-
|
20 |
-
# Configure logging
|
21 |
-
logging.basicConfig(level=logging.INFO)
|
22 |
-
logger = logging.getLogger(__name__)
|
23 |
-
|
24 |
-
# Add src directory to Python path
|
25 |
-
src_path = Path(__file__).parent.parent
|
26 |
-
sys.path.append(str(src_path))
|
27 |
-
|
28 |
-
|
29 |
-
# Get secrets from Hugging Face Space
|
30 |
-
def get_secrets():
|
31 |
-
"""Get secrets from Hugging Face Space or local environment."""
|
32 |
-
|
33 |
-
return {
|
34 |
-
"OPENAI_API_KEY": os.getenv("OPENAI_API_KEY"),
|
35 |
-
"LANGCHAIN_API_KEY": os.getenv("LANGCHAIN_API_KEY"),
|
36 |
-
"LANGCHAIN_PROJECT": os.getenv("LANGCHAIN_PROJECT", "grid-code-assistant"),
|
37 |
-
"LANGCHAIN_TRACING_V2": os.getenv("LANGCHAIN_TRACING_V2", "true"),
|
38 |
-
}
|
39 |
-
|
40 |
-
|
41 |
-
# Set up environment variables from secrets
|
42 |
-
secrets = get_secrets()
|
43 |
-
for key, value in secrets.items():
|
44 |
-
if value:
|
45 |
-
os.environ[key] = value
|
46 |
-
|
47 |
-
# Verify API keys
|
48 |
-
if not os.environ.get("OPENAI_API_KEY"):
|
49 |
-
st.error(
|
50 |
-
"OpenAI API key not found. Please set it in the Hugging Face Space secrets."
|
51 |
-
)
|
52 |
-
st.stop()
|
53 |
-
|
54 |
-
from embedding.model import EmbeddingModel
|
55 |
-
from rag.chain import RAGChain
|
56 |
-
from rag.document_loader import GridCodeLoader
|
57 |
-
from rag.vectorstore import VectorStore
|
58 |
-
|
59 |
-
|
60 |
-
class WeatherTool:
|
61 |
-
def __init__(self):
|
62 |
-
self.base_url = "https://api.weather.gov"
|
63 |
-
self.headers = {
|
64 |
-
"User-Agent": "(Grid Code Assistant, [email protected])",
|
65 |
-
"Accept": "application/json",
|
66 |
-
}
|
67 |
-
|
68 |
-
def get_coordinates_from_zip(self, zipcode):
|
69 |
-
response = requests.get(f"https://api.zippopotam.us/us/{zipcode}")
|
70 |
-
if response.status_code == 200:
|
71 |
-
data = response.json()
|
72 |
-
return {
|
73 |
-
"lat": data["places"][0]["latitude"],
|
74 |
-
"lon": data["places"][0]["longitude"],
|
75 |
-
"place": data["places"][0]["place name"],
|
76 |
-
"state": data["places"][0]["state"],
|
77 |
-
}
|
78 |
-
return None
|
79 |
-
|
80 |
-
def run(self, zipcode):
|
81 |
-
coords = self.get_coordinates_from_zip(zipcode)
|
82 |
-
if not coords:
|
83 |
-
return {"error": "Invalid ZIP code or unable to get coordinates."}
|
84 |
-
|
85 |
-
point_url = f"{self.base_url}/points/{coords['lat']},{coords['lon']}"
|
86 |
-
response = requests.get(point_url, headers=self.headers)
|
87 |
-
|
88 |
-
if response.status_code != 200:
|
89 |
-
return {"error": "Unable to fetch weather data."}
|
90 |
-
|
91 |
-
grid_data = response.json()
|
92 |
-
forecast_url = grid_data["properties"]["forecast"]
|
93 |
-
|
94 |
-
response = requests.get(forecast_url, headers=self.headers)
|
95 |
-
if response.status_code == 200:
|
96 |
-
forecast_data = response.json()["properties"]["periods"]
|
97 |
-
weather_data = {
|
98 |
-
"type": "weather",
|
99 |
-
"location": f"{coords['place']}, {coords['state']}",
|
100 |
-
"current": forecast_data[0],
|
101 |
-
"forecast": forecast_data[1:4],
|
102 |
-
}
|
103 |
-
# Save to session state
|
104 |
-
st.session_state.weather_data = weather_data
|
105 |
-
return weather_data
|
106 |
-
return {"error": "Unable to fetch forecast data."}
|
107 |
-
|
108 |
-
|
109 |
-
def initialize_rag():
|
110 |
-
"""Initialize RAG system."""
|
111 |
-
if "rag_chain" in st.session_state:
|
112 |
-
logger.info("Using cached RAG chain from session state")
|
113 |
-
return st.session_state.rag_chain
|
114 |
-
|
115 |
-
# Use relative path from src directory
|
116 |
-
data_path = Path(__file__).parent.parent.parent / "app" / "data" / "raw" / "grid_code.pdf"
|
117 |
-
if not data_path.exists():
|
118 |
-
raise FileNotFoundError(f"PDF not found: {data_path}")
|
119 |
-
|
120 |
-
with st.spinner("Loading Grid Code documents..."):
|
121 |
-
loader = GridCodeLoader(str(data_path), pages=17)
|
122 |
-
documents = loader.load_and_split()
|
123 |
-
logger.info(f"Loaded {len(documents)} document chunks")
|
124 |
-
|
125 |
-
with st.spinner("Creating vector store..."):
|
126 |
-
embedding_model = EmbeddingModel()
|
127 |
-
vectorstore = VectorStore(embedding_model)
|
128 |
-
vectorstore = vectorstore.create_vectorstore(documents)
|
129 |
-
logger.info("Vector store created successfully")
|
130 |
-
|
131 |
-
# Cache the RAG chain in session state
|
132 |
-
rag_chain = RAGChain(vectorstore)
|
133 |
-
st.session_state.rag_chain = rag_chain
|
134 |
-
return rag_chain
|
135 |
-
|
136 |
-
|
137 |
-
class RAGTool:
|
138 |
-
def __init__(self, rag_chain):
|
139 |
-
self.rag_chain = rag_chain
|
140 |
-
|
141 |
-
def run(self, question: str) -> str:
|
142 |
-
"""Answer questions using the Grid Code."""
|
143 |
-
response = self.rag_chain.invoke(question)
|
144 |
-
return response["answer"]
|
145 |
-
|
146 |
-
|
147 |
-
class AgentState(TypedDict):
|
148 |
-
"""State definition for the agent."""
|
149 |
-
|
150 |
-
messages: Annotated[list, add_messages]
|
151 |
-
|
152 |
-
|
153 |
-
def create_agent_workflow(rag_chain, weather_tool):
|
154 |
-
"""Create an agent that can use both RAG and weather tools."""
|
155 |
-
|
156 |
-
# Define the tools
|
157 |
-
tools = [
|
158 |
-
Tool(
|
159 |
-
name="grid_code_query",
|
160 |
-
description="Answer questions about the Grid Code and electrical regulations",
|
161 |
-
func=lambda q: rag_chain.invoke(q)["answer"],
|
162 |
-
),
|
163 |
-
Tool(
|
164 |
-
name="get_weather",
|
165 |
-
description="Get weather forecast for a ZIP code. Input should be a 5-digit ZIP code.",
|
166 |
-
func=lambda z: weather_tool.run(z),
|
167 |
-
),
|
168 |
-
]
|
169 |
-
|
170 |
-
# Initialize the LLM
|
171 |
-
llm = ChatOpenAI(model="gpt-4o", temperature=0)
|
172 |
-
|
173 |
-
# Create the custom prompt
|
174 |
-
prompt = ChatPromptTemplate.from_messages(
|
175 |
-
[
|
176 |
-
SystemMessagePromptTemplate.from_template(
|
177 |
-
"""You are a helpful assistant that specializes in two areas:
|
178 |
-
1. Answering questions about electrical Grid Code regulations
|
179 |
-
2. Providing weather information for specific locations
|
180 |
-
|
181 |
-
For weather queries:
|
182 |
-
- Extract the ZIP code from the question
|
183 |
-
- Use the get_weather tool to fetch the forecast
|
184 |
-
|
185 |
-
For Grid Code questions:
|
186 |
-
- Use the grid_code_query tool to find relevant information
|
187 |
-
- If the information isn't in the Grid Code, clearly state that
|
188 |
-
- Provide specific references when possible
|
189 |
-
"""
|
190 |
-
),
|
191 |
-
MessagesPlaceholder(variable_name="chat_history", optional=True),
|
192 |
-
HumanMessagePromptTemplate.from_template("{input}"),
|
193 |
-
MessagesPlaceholder(variable_name="agent_scratchpad"),
|
194 |
-
]
|
195 |
-
)
|
196 |
-
|
197 |
-
# Create the agent
|
198 |
-
agent = create_tool_calling_agent(llm, tools, prompt)
|
199 |
-
|
200 |
-
return AgentExecutor(
|
201 |
-
agent=agent,
|
202 |
-
tools=tools,
|
203 |
-
verbose=True,
|
204 |
-
handle_parsing_errors=True,
|
205 |
-
)
|
206 |
-
|
207 |
-
|
208 |
-
def display_weather(weather_data):
|
209 |
-
"""Display weather information in a nice format"""
|
210 |
-
if "error" in weather_data:
|
211 |
-
st.error(weather_data["error"])
|
212 |
-
return
|
213 |
-
|
214 |
-
if weather_data.get("type") == "weather":
|
215 |
-
# Location header
|
216 |
-
st.header(f"Weather for {weather_data['location']}")
|
217 |
-
|
218 |
-
# Current conditions
|
219 |
-
current = weather_data["current"]
|
220 |
-
st.subheader("Current Conditions")
|
221 |
-
|
222 |
-
# Use columns for current weather layout
|
223 |
-
col1, col2 = st.columns(2)
|
224 |
-
|
225 |
-
with col1:
|
226 |
-
# Temperature display with metric
|
227 |
-
st.metric(
|
228 |
-
"Temperature", f"{current['temperature']}°{current['temperatureUnit']}"
|
229 |
-
)
|
230 |
-
# Wind information
|
231 |
-
st.info(f"💨 Wind: {current['windSpeed']} {current['windDirection']}")
|
232 |
-
|
233 |
-
with col2:
|
234 |
-
# Current forecast
|
235 |
-
st.markdown(f"**🌤️ Conditions:** {current['shortForecast']}")
|
236 |
-
st.markdown(f"**📝 Details:** {current['detailedForecast']}")
|
237 |
-
|
238 |
-
# Extended forecast
|
239 |
-
st.subheader("Extended Forecast")
|
240 |
-
for period in weather_data["forecast"]:
|
241 |
-
with st.expander(f"📅 {period['name']}"):
|
242 |
-
st.markdown(
|
243 |
-
f"**🌡️ Temperature:** {period['temperature']}°{period['temperatureUnit']}"
|
244 |
-
)
|
245 |
-
st.markdown(
|
246 |
-
f"**💨 Wind:** {period['windSpeed']} {period['windDirection']}"
|
247 |
-
)
|
248 |
-
st.markdown(f"**🌤️ Forecast:** {period['shortForecast']}")
|
249 |
-
st.markdown(f"**📝 Details:** {period['detailedForecast']}")
|
250 |
-
|
251 |
-
|
252 |
-
def main():
|
253 |
-
st.title("GridGuide: Field Assistant")
|
254 |
-
|
255 |
-
# Initialize if not in session state
|
256 |
-
if "app" not in st.session_state:
|
257 |
-
rag_chain = initialize_rag()
|
258 |
-
weather_tool = WeatherTool()
|
259 |
-
st.session_state.app = create_agent_workflow(rag_chain, weather_tool)
|
260 |
-
|
261 |
-
# Create the input box
|
262 |
-
user_input = st.text_input("Ask about weather or the Grid Code:")
|
263 |
-
|
264 |
-
if user_input:
|
265 |
-
with st.spinner("Processing your request..."):
|
266 |
-
# Invoke the agent executor
|
267 |
-
result = st.session_state.app.invoke({"input": user_input})
|
268 |
-
|
269 |
-
# Check if we have weather data in session state
|
270 |
-
if "weather_data" in st.session_state:
|
271 |
-
display_weather(st.session_state.weather_data)
|
272 |
-
# Clear the weather data after displaying
|
273 |
-
del st.session_state.weather_data
|
274 |
-
else:
|
275 |
-
st.write(result["output"])
|
276 |
-
|
277 |
-
|
278 |
-
if __name__ == "__main__":
|
279 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|