ddooling commited on
Commit
bcbb2ee
·
verified ·
1 Parent(s): abb65c8

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +167 -191
app.py CHANGED
@@ -46,15 +46,15 @@ PDF_DIR = "usedpdfs" # Replace with your directory path
46
  DEFAULT_PDF = "s41597-024-03770-7.pdf" # Replace with your actual PDF filename
47
 
48
 
 
49
  # Ensure the PDF_DIR exists
50
  if not os.path.isdir(PDF_DIR):
51
- raise ValueError(
52
- f"The directory '{PDF_DIR}' does not exist. Please check the path."
53
- )
54
 
55
 
56
  # Get list of PDF files in the directory
57
- pdf_files = [f for f in os.listdir(PDF_DIR) if f.lower().endswith(".pdf")]
58
 
59
  if DEFAULT_PDF not in pdf_files:
60
  raise ValueError(f"Default PDF '{DEFAULT_PDF}' not found in '{PDF_DIR}'.")
@@ -63,7 +63,6 @@ if DEFAULT_PDF not in pdf_files:
63
  if not pdf_files:
64
  raise ValueError(f"No PDF files found in the directory '{PDF_DIR}'.")
65
 
66
-
67
  def display_pdf(selected_file):
68
  """
69
  Given the selected file name, return the full path to display in the PDF viewer.
@@ -72,6 +71,9 @@ def display_pdf(selected_file):
72
  return file_path
73
 
74
 
 
 
 
75
  def web_search(query: str) -> str:
76
  """
77
  Performs a web search using the Tavily API and returns the context string.
@@ -95,20 +97,18 @@ def web_search(query: str) -> str:
95
  return f"Error performing web search: {str(e)}"
96
 
97
 
 
98
  # Ensure the PDF_DIR exists
99
  if not os.path.isdir(PDF_DIR):
100
- raise ValueError(
101
- f"The directory '{PDF_DIR}' does not exist. Please check the path."
102
- )
103
 
104
  # Get list of PDF files in the directory
105
- pdf_files = [f for f in os.listdir(PDF_DIR) if f.lower().endswith(".pdf")]
106
 
107
  # Check if there are PDF files in the directory
108
  if not pdf_files:
109
  raise ValueError(f"No PDF files found in the directory '{PDF_DIR}'.")
110
 
111
-
112
  def display_pdf(selected_file):
113
  """
114
  Given the selected file name, return the full path to display in the PDF viewer.
@@ -117,6 +117,7 @@ def display_pdf(selected_file):
117
  return file_path
118
 
119
 
 
120
  # Function to generate a date range
121
  def generate_date_range(start_date, end_date, freq="D"):
122
  return pd.date_range(start=start_date, end=end_date, freq=freq)
@@ -188,21 +189,20 @@ synthetic_dataset["time"] = [
188
  ]
189
 
190
  # something whcky happened with the vector store. i don't know what the fuck happened.
191
- # have to create a new assistant.
192
 
193
  # you need to have system instructions ilke this
194
- # You are a helpful assistant and expert at ansewring building automation questions. Always carry out a file search for the desired information. You can augment that information with your general knowledge, but alwasy carry out a file seaach with every query first to see if the relevant information is there, and then add to that afterwards.
195
 
196
  # name : Building Energy and Efficiency Expert
197
 
198
  # And also added repitiion of the instructions in the thread / run creation.
199
 
200
- VECTOR_STORE_ID = os.environ[
201
- "VECTOR_STORE_ID"
202
- ] # will need to be updated. what the hell happened??
203
  ASSISTANT_ID = os.environ["ASSISTANT_ID"]
204
 
205
 
 
206
  # small medium offices are waht is relevant to this dataset.
207
 
208
  # Initialize the client
@@ -228,43 +228,37 @@ class EventHandler(AssistantEventHandler):
228
  def on_text_delta(self, delta, snapshot):
229
  text = delta.value
230
  self.response_queue.put(text)
231
-
232
  @override
233
  def on_event(self, event):
234
- # Retrieve events that are denoted with 'requires_action'
235
- # since these will have our tool_calls
236
- if event.event == "thread.run.requires_action":
237
- run_id = event.data.id # Retrieve the run ID from the event data
238
- self.handle_requires_action(event.data, run_id)
239
-
240
  def handle_requires_action(self, data, run_id):
241
- tool_outputs = []
242
-
243
- for tool in data.required_action.submit_tool_outputs.tool_calls:
244
- if tool.function.name == "update_weather_forecast":
245
- print(tool.function.arguments)
246
- args = json.loads(tool.function.arguments)
247
- loc = args["location"]
248
- tool_outputs.append(
249
- {"tool_call_id": tool.id, "output": update_weather_forecast(loc)}
250
- )
251
- elif tool.function.name == "update_weather":
252
- print(tool.function.arguments)
253
- args = json.loads(tool.function.arguments)
254
- loc = args["location"]
255
- tool_outputs.append(
256
- {"tool_call_id": tool.id, "output": update_weather(loc)}
257
- )
258
- elif tool.function.name == "web_search":
259
- print(tool.function.arguments)
260
- args = json.loads(tool.function.arguments)
261
- query = args["query"]
262
- tool_outputs.append(
263
- {"tool_call_id": tool.id, "output": web_search(query)}
264
- )
265
-
266
- # Submit all tool_outputs at the same time
267
- self.submit_tool_outputs(tool_outputs, run_id)
268
 
269
  def submit_tool_outputs(self, tool_outputs, run_id):
270
  # Use the submit_tool_outputs_stream helper
@@ -307,7 +301,7 @@ def chat(usr_message, history):
307
  with client.beta.threads.runs.stream(
308
  thread_id=thread_id,
309
  assistant_id=ASSISTANT_ID,
310
- tool_choice="required",
311
  event_handler=EventHandler(response_queue),
312
  ) as stream:
313
  stream.until_done()
@@ -351,10 +345,8 @@ def update_weather(location):
351
  visibility = weather_data["visibility"]
352
  wind_speed = weather_data["wind"]["speed"]
353
  wind_deg = weather_data["wind"]["deg"]
354
- sunrise = datetime.fromtimestamp(weather_data["sys"]["sunrise"]).strftime(
355
- "%H:%M:%S"
356
- )
357
- sunset = datetime.fromtimestamp(weather_data["sys"]["sunset"]).strftime("%H:%M:%S")
358
  temp = weather_data["main"]["temp"]
359
  humidity = weather_data["main"]["humidity"]
360
  condition = weather_data["weather"][0]["description"]
@@ -371,8 +363,9 @@ def update_weather(location):
371
  - **Sunrise:** {sunrise}, **Sunset:** {sunset}"""
372
 
373
 
 
374
  def update_weather_forecast(location: str) -> str:
375
- """Fetches the weather forecast for a given location and returns a formatted string
376
  Parameters:
377
  - location: the search term to find weather information
378
  Returns:
@@ -385,7 +378,7 @@ def update_weather_forecast(location: str) -> str:
385
  "q": location,
386
  "appid": api_key,
387
  "units": "imperial",
388
- "cnt": 40, # Request 40 data points (5 days * 8 three-hour periods)
389
  }
390
  response = requests.get(base_url, params=params)
391
  weather_data = response.json()
@@ -394,36 +387,32 @@ def update_weather_forecast(location: str) -> str:
394
 
395
  # Organize forecast data per date
396
  forecast_data = {}
397
- for item in weather_data["list"]:
398
- dt_txt = item["dt_txt"] # 'YYYY-MM-DD HH:MM:SS'
399
- date_str = dt_txt.split(" ")[0] # 'YYYY-MM-DD'
400
- time_str = dt_txt.split(" ")[1] # 'HH:MM:SS'
401
  forecast_data.setdefault(date_str, [])
402
- forecast_data[date_str].append(
403
- {
404
- "time": time_str,
405
- "temp": item["main"]["temp"],
406
- "feels_like": item["main"]["feels_like"],
407
- "humidity": item["main"]["humidity"],
408
- "pressure": item["main"]["pressure"],
409
- "wind_speed": item["wind"]["speed"],
410
- "wind_deg": item["wind"]["deg"],
411
- "condition": item["weather"][0]["description"],
412
- "visibility": item.get(
413
- "visibility", "N/A"
414
- ), # sometimes visibility may be missing
415
- }
416
- )
417
 
418
  # Process data to create daily summaries
419
  daily_summaries = {}
420
  for date_str, forecasts in forecast_data.items():
421
- temps = [f["temp"] for f in forecasts]
422
- feels_likes = [f["feels_like"] for f in forecasts]
423
- humidities = [f["humidity"] for f in forecasts]
424
- pressures = [f["pressure"] for f in forecasts]
425
- wind_speeds = [f["wind_speed"] for f in forecasts]
426
- conditions = [f["condition"] for f in forecasts]
427
 
428
  min_temp = min(temps)
429
  max_temp = max(temps)
@@ -438,18 +427,18 @@ def update_weather_forecast(location: str) -> str:
438
  most_common_condition = condition_counts.most_common(1)[0][0]
439
 
440
  daily_summaries[date_str] = {
441
- "min_temp": min_temp,
442
- "max_temp": max_temp,
443
- "avg_temp": avg_temp,
444
- "avg_feels_like": avg_feels_like,
445
- "avg_humidity": avg_humidity,
446
- "avg_pressure": avg_pressure,
447
- "avg_wind_speed": avg_wind_speed,
448
- "condition": most_common_condition,
449
  }
450
 
451
  # Build the formatted string
452
- city_name = weather_data["city"]["name"]
453
  ret_str = f"**5-Day Weather Forecast for {city_name}:**\n"
454
 
455
  for date_str in sorted(daily_summaries.keys()):
@@ -466,14 +455,14 @@ def update_weather_forecast(location: str) -> str:
466
  return ret_str
467
 
468
 
469
- llmmodel = OpenAI(api_token=os.environ["OPENAI_API_KEY"], model="gpt-4o")
470
 
471
  # Load dataframes
472
  dfcleaned = pd.read_csv("dfcleaned.csv")
473
- dfcleaned["Timestamp"] = pd.to_datetime(dfcleaned["Timestamp"])
474
- dfcleaned["off-nominal"] = dfcleaned["off-nominal"].apply(str)
475
  dfshaps = pd.read_csv("shaps.csv")
476
- dfshaps["Timestamp"] = pd.to_datetime(dfshaps["Timestamp"])
477
 
478
  # Initialize Agent
479
  agent = Agent([dfcleaned, dfshaps], config={"llm": llmmodel})
@@ -482,10 +471,11 @@ sdfshaps = SmartDataframe(dfshaps, config={"llm": llmmodel})
482
  sdfcleaned = SmartDataframe(dfcleaned, config={"llm": llmmodel})
483
 
484
 
 
485
  def process_query(query):
486
  response = agent.chat(query) # Replace with your actual agent chat implementation
487
  print(response)
488
-
489
  # Initialize outputs and visibility flags
490
  text_output = None
491
  image_output = None
@@ -493,27 +483,28 @@ def process_query(query):
493
  text_visible = False
494
  image_visible = False
495
  dataframe_visible = False
496
-
497
  if isinstance(response, str) and ".png" not in response:
498
  text_output = response
499
  text_visible = True
500
  elif isinstance(response, str) and ".png" in response:
501
- image_output = response # Assuming response is a filepath or URL to the image
502
- image_visible = True
503
  elif isinstance(response, pd.DataFrame):
504
  dataframe_output = response
505
  dataframe_visible = True
506
-
507
  return (
508
  text_output,
509
  image_output,
510
  dataframe_output,
511
  gr.update(visible=text_visible),
512
  gr.update(visible=image_visible),
513
- gr.update(visible=dataframe_visible),
514
  )
515
 
516
 
 
517
  def gradio_app():
518
  iface = gr.Interface(
519
  fn=process_query,
@@ -521,33 +512,24 @@ def gradio_app():
521
  outputs=[
522
  gr.Textbox(label="Response"),
523
  gr.Image(label="Plot"),
524
- gr.DataFrame(label="Dataframe"),
525
  ],
526
  title="pandasai Query Processor",
527
- description="Enter your query related to the csv data files.",
528
  )
529
  return iface
530
 
531
-
532
  with gr.Blocks(
533
- # theme=gr.themes.Monochrome(primary_hue="green"),
534
- theme=gr.themes.Soft(),
535
  ) as demo:
536
  with gr.Row(): # Combine the two weather functions into a single row
537
  with gr.Column():
538
- location1 = gr.Textbox(
539
- label="Enter location for weather (e.g., Rio Rancho, New Mexico)",
540
- value="Cambridge, Massachusetts",
541
- )
542
  weather_button = gr.Button("Get Weather")
543
- # output1 = gr.Markdown(label="Weather Information")
544
- output1 = gr.Textbox(
545
- label="Weather Information",
546
- lines=8,
547
- max_lines=8,
548
- show_label=True,
549
- show_copy_button=True,
550
- )
551
  weather_button.click(
552
  fn=update_weather,
553
  inputs=location1,
@@ -555,19 +537,12 @@ with gr.Blocks(
555
  api_name="update_weather",
556
  )
557
  with gr.Column():
558
- location2 = gr.Textbox(
559
- label="Enter location for weather forecast (e.g., Rio Rancho, New Mexico)",
560
- value="Cambridge, Massachusetts",
561
- )
562
  weather_forecast_button = gr.Button("Get 5-Day Weather Forecast")
563
- # output2 = gr.Markdown(label="Weather Forecast Information")
564
- output2 = gr.Textbox(
565
- label="Weather 5-Day Forecast Information",
566
- lines=8,
567
- max_lines=8,
568
- show_label=True,
569
- show_copy_button=True,
570
- )
571
  weather_forecast_button.click(
572
  fn=update_weather_forecast,
573
  inputs=location2,
@@ -576,7 +551,7 @@ with gr.Blocks(
576
  )
577
  gr.Markdown("# 📄 PDF Viewer Section")
578
  gr.Markdown("Select a PDF from the dropdown below to view it.")
579
-
580
  with gr.Accordion("Open PDF Selection", open=False):
581
  with gr.Row():
582
  # Assign a larger scale to the dropdown
@@ -584,20 +559,22 @@ with gr.Blocks(
584
  choices=pdf_files,
585
  label="Select a PDF",
586
  value=DEFAULT_PDF, # Set a default value
587
- scale=1, # This component takes twice the space
588
  )
589
  # Assign a smaller scale to the PDF viewer
590
  pdf_viewer = PDF(
591
  label="PDF Viewer",
592
  interactive=True,
593
- scale=3,
594
- value=display_pdf(
595
- DEFAULT_PDF
596
- ), # This component takes half the space compared to dropdown
597
  )
598
-
599
  # Set up the event: when dropdown changes, update the PDF viewer
600
- dropdown.change(fn=display_pdf, inputs=dropdown, outputs=pdf_viewer)
 
 
 
 
601
  with gr.Row():
602
  with gr.Column(scale=1):
603
  gr.Markdown("# Building Automation Assistant")
@@ -611,17 +588,13 @@ with gr.Blocks(
611
  # Update the ChatInterface to handle streaming
612
  chat_interface = gr.ChatInterface(
613
  chat,
614
- # show_label=True,
615
- # show_copy_button=True,
616
- chatbot=gr.Chatbot(
617
- height=750,
618
- show_copy_button=True,
619
- show_copy_all_button=True,
620
- avatar_images=("user_avatar.png", "assistant_avatar.png"),
621
- ),
622
  title="Ask Me Anything",
623
- examples_per_page=5,
624
- # theme="soft", # glass
625
  description="Type your question about building automation here.",
626
  examples=[
627
  "Give the weather forecast for Cambridge, MA",
@@ -634,20 +607,20 @@ with gr.Blocks(
634
  "What are the most common maintenance challenges faced by net-zero energy buildings?",
635
  "How does the Uponor Climate Control Network System contribute to building maintenance?",
636
  "What role do smart systems play in the maintenance of sustainable buildings like HouseZero?",
637
- "Can you provide data on the energy performance of HouseZero over the past year?",
638
- "Tell me about the HouseZero dataset. Retrieve information from the publication you have access to. Use your file retrieval tool.",
639
  "Describe in detail the relationshp between the columns and values in the uploaded CSV files and the information you have access to regarding the HouseZero dataset. Be verbose. Use your file retrieval tool.",
640
  "Please comment on the zone relative humidity features, specifically if they indicate a problem withthe building",
641
  "Give me in great detail any advice you have to maintain a small to midsize office building, like the HouseZero data corresponds to. Be verbose. Use your file retrieval tool.",
642
  "Is there any information in the datafiles that indicates a problem with the building?",
643
  "Show Massachusetts electricity billing rates during the same time span as the CSV data",
644
  "Use those rates and the relevant columns in the CSV files to estimate how much it costs to operate this building per month.",
645
- "What is the estimated average electricity cost for operating the building using massachusetts energy rates. use your file retrieval tool. use data csv files for building data. Limit your response to 140 characters. Use your file retrieval tool.",
646
  "Based on the data in these CSV files, can you assign an EnergyIQ score from 1-10 that reflects how well the building is operating? Explain the reason for your score and provide any recommendations on actions to take that can improve it in the future. Be verbose. Use your file retrieval tool.",
647
  "Please summarize information concerning sensor networks that may be leading to faulty meaurements.",
648
  "Tell me how to properly install the PVC sky lights.",
649
  "Based on data and insights, what specific changes should be made to HouseZero's maintenance protocols?",
650
- "what recommendations do you have to mitigate against high relative humidity zone measurements in structures like the housezero building?",
651
  ],
652
  fill_height=True,
653
  )
@@ -658,13 +631,13 @@ with gr.Blocks(
658
  # with gr.Column():
659
  # Define the three ScatterPlot components
660
  anomaly_plot = gr.ScatterPlot(
661
- dfcleaned,
662
- x="Timestamp",
663
- y="Z5_RH",
664
  color="off-nominal",
665
- title="Anomaly Score",
666
  )
667
-
668
  zone3_plot = gr.ScatterPlot(
669
  dfcleaned,
670
  x="Timestamp",
@@ -680,8 +653,8 @@ with gr.Blocks(
680
  color="off-nominal",
681
  title="Zone 4 Relative Humidity",
682
  )
683
-
684
- # Group all plots into a list for easy management
685
  plots = [anomaly_plot, zone3_plot, zone4_plot]
686
 
687
  def select_region(selection: gr.SelectData):
@@ -696,9 +669,9 @@ with gr.Blocks(
696
  """
697
  if selection is None or selection.index is None:
698
  return [gr.Plot.update() for _ in plots]
699
-
700
  min_x, max_x = selection.index
701
- # Update the x_lim for each plot
702
  return [gr.ScatterPlot(x_lim=(min_x, max_x)) for _ in plots]
703
 
704
  def reset_region():
@@ -710,35 +683,37 @@ with gr.Blocks(
710
  """
711
  return [gr.ScatterPlot(x_lim=None) for _ in plots]
712
 
713
- # Attach event listeners to each plot
714
  for plot in plots:
715
  plot.select(
716
- select_region, inputs=None, outputs=plots # Update all plots
 
 
717
  )
718
  plot.double_click(
719
- reset_region, inputs=None, outputs=plots # Reset all plots
 
 
720
  )
721
 
722
- # plots = [plt, first_plot, second_plot]
723
-
724
- # def select_region(selection: gr.SelectData):
725
- # min_w, max_w = selection.index
726
- # return gr.ScatterPlot(x_lim=(min_w, max_w))
727
 
728
- # for p in plots:
729
- # p.select(select_region, None, plots)
730
- # p.double_click(lambda: [gr.LinePlot(x_lim=None)] * len(plots), None, plots)
731
 
732
- # second_plot.select(select_second_region, None, plt)
733
- # second_plot.double_click(lambda: gr.ScatterPlot(x_lim=None), None, plt)
734
- # gr.Column([anomaly_plot, first_plot, second_plot])
 
 
 
 
735
 
736
- # anomaly_info = gr.Markdown("Anomaly detected around October 15, 2023")
737
  with gr.Column():
738
- query = gr.Textbox(
739
- label="Enter your question about the data",
740
- value="Plot the anomaly_score as a function of time and highlight the highest 20 values",
741
- )
742
  query_button = gr.Button("Submit Data Query")
743
  with gr.Row():
744
  with gr.Column(visible=False) as output_col1:
@@ -751,21 +726,22 @@ with gr.Blocks(
751
  fn=process_query,
752
  inputs=query,
753
  outputs=[
754
- out1, # Text output
755
- out2, # Image output
756
- out3, # DataFrame output
757
- output_col1, # Visibility for Text output
758
- output_col2, # Visibility for Image output
759
- output_col3, # Visibility for DataFrame output
760
  ],
761
- api_name="process_query",
762
  )
763
-
764
  # hide visibility until its ready
765
-
 
766
  # Weather input
767
- # with gr.Row():
768
- # iface = gradio_app()
769
 
770
 
771
- demo.launch(share=True)
 
46
  DEFAULT_PDF = "s41597-024-03770-7.pdf" # Replace with your actual PDF filename
47
 
48
 
49
+
50
  # Ensure the PDF_DIR exists
51
  if not os.path.isdir(PDF_DIR):
52
+ raise ValueError(f"The directory '{PDF_DIR}' does not exist. Please check the path.")
53
+
 
54
 
55
 
56
  # Get list of PDF files in the directory
57
+ pdf_files = [f for f in os.listdir(PDF_DIR) if f.lower().endswith('.pdf')]
58
 
59
  if DEFAULT_PDF not in pdf_files:
60
  raise ValueError(f"Default PDF '{DEFAULT_PDF}' not found in '{PDF_DIR}'.")
 
63
  if not pdf_files:
64
  raise ValueError(f"No PDF files found in the directory '{PDF_DIR}'.")
65
 
 
66
  def display_pdf(selected_file):
67
  """
68
  Given the selected file name, return the full path to display in the PDF viewer.
 
71
  return file_path
72
 
73
 
74
+
75
+
76
+
77
  def web_search(query: str) -> str:
78
  """
79
  Performs a web search using the Tavily API and returns the context string.
 
97
  return f"Error performing web search: {str(e)}"
98
 
99
 
100
+
101
  # Ensure the PDF_DIR exists
102
  if not os.path.isdir(PDF_DIR):
103
+ raise ValueError(f"The directory '{PDF_DIR}' does not exist. Please check the path.")
 
 
104
 
105
  # Get list of PDF files in the directory
106
+ pdf_files = [f for f in os.listdir(PDF_DIR) if f.lower().endswith('.pdf')]
107
 
108
  # Check if there are PDF files in the directory
109
  if not pdf_files:
110
  raise ValueError(f"No PDF files found in the directory '{PDF_DIR}'.")
111
 
 
112
  def display_pdf(selected_file):
113
  """
114
  Given the selected file name, return the full path to display in the PDF viewer.
 
117
  return file_path
118
 
119
 
120
+
121
  # Function to generate a date range
122
  def generate_date_range(start_date, end_date, freq="D"):
123
  return pd.date_range(start=start_date, end=end_date, freq=freq)
 
189
  ]
190
 
191
  # something whcky happened with the vector store. i don't know what the fuck happened.
192
+ # have to create a new assistant.
193
 
194
  # you need to have system instructions ilke this
195
+ # You are a helpful assistant and expert at ansewring building automation questions. Always carry out a file search for the desired information. You can augment that information with your general knowledge, but alwasy carry out a file seaach with every query first to see if the relevant information is there, and then add to that afterwards.
196
 
197
  # name : Building Energy and Efficiency Expert
198
 
199
  # And also added repitiion of the instructions in the thread / run creation.
200
 
201
+ VECTOR_STORE_ID = os.environ["VECTOR_STORE_ID"] # will need to be updated. what the hell happened??
 
 
202
  ASSISTANT_ID = os.environ["ASSISTANT_ID"]
203
 
204
 
205
+
206
  # small medium offices are waht is relevant to this dataset.
207
 
208
  # Initialize the client
 
228
  def on_text_delta(self, delta, snapshot):
229
  text = delta.value
230
  self.response_queue.put(text)
231
+
232
  @override
233
  def on_event(self, event):
234
+ # Retrieve events that are denoted with 'requires_action'
235
+ # since these will have our tool_calls
236
+ if event.event == 'thread.run.requires_action':
237
+ run_id = event.data.id # Retrieve the run ID from the event data
238
+ self.handle_requires_action(event.data, run_id)
239
+
240
  def handle_requires_action(self, data, run_id):
241
+ tool_outputs = []
242
+
243
+ for tool in data.required_action.submit_tool_outputs.tool_calls:
244
+ if tool.function.name == "update_weather_forecast":
245
+ print(tool.function.arguments)
246
+ args = json.loads(tool.function.arguments)
247
+ loc = args["location"]
248
+ tool_outputs.append({"tool_call_id": tool.id, "output": update_weather_forecast(loc)})
249
+ elif tool.function.name == "update_weather":
250
+ print(tool.function.arguments)
251
+ args = json.loads(tool.function.arguments)
252
+ loc = args["location"]
253
+ tool_outputs.append({"tool_call_id": tool.id, "output": update_weather(loc)})
254
+ elif tool.function.name == "web_search":
255
+ print(tool.function.arguments)
256
+ args = json.loads(tool.function.arguments)
257
+ query = args["query"]
258
+ tool_outputs.append({"tool_call_id": tool.id, "output": web_search(query)})
259
+
260
+ # Submit all tool_outputs at the same time
261
+ self.submit_tool_outputs(tool_outputs, run_id)
 
 
 
 
 
 
262
 
263
  def submit_tool_outputs(self, tool_outputs, run_id):
264
  # Use the submit_tool_outputs_stream helper
 
301
  with client.beta.threads.runs.stream(
302
  thread_id=thread_id,
303
  assistant_id=ASSISTANT_ID,
304
+ tool_choice = "required",
305
  event_handler=EventHandler(response_queue),
306
  ) as stream:
307
  stream.until_done()
 
345
  visibility = weather_data["visibility"]
346
  wind_speed = weather_data["wind"]["speed"]
347
  wind_deg = weather_data["wind"]["deg"]
348
+ sunrise = datetime.fromtimestamp(weather_data["sys"]["sunrise"]).strftime('%H:%M:%S')
349
+ sunset = datetime.fromtimestamp(weather_data["sys"]["sunset"]).strftime('%H:%M:%S')
 
 
350
  temp = weather_data["main"]["temp"]
351
  humidity = weather_data["main"]["humidity"]
352
  condition = weather_data["weather"][0]["description"]
 
363
  - **Sunrise:** {sunrise}, **Sunset:** {sunset}"""
364
 
365
 
366
+
367
  def update_weather_forecast(location: str) -> str:
368
+ """ Fetches the weather forecast for a given location and returns a formatted string
369
  Parameters:
370
  - location: the search term to find weather information
371
  Returns:
 
378
  "q": location,
379
  "appid": api_key,
380
  "units": "imperial",
381
+ "cnt": 40 # Request 40 data points (5 days * 8 three-hour periods)
382
  }
383
  response = requests.get(base_url, params=params)
384
  weather_data = response.json()
 
387
 
388
  # Organize forecast data per date
389
  forecast_data = {}
390
+ for item in weather_data['list']:
391
+ dt_txt = item['dt_txt'] # 'YYYY-MM-DD HH:MM:SS'
392
+ date_str = dt_txt.split(' ')[0] # 'YYYY-MM-DD'
393
+ time_str = dt_txt.split(' ')[1] # 'HH:MM:SS'
394
  forecast_data.setdefault(date_str, [])
395
+ forecast_data[date_str].append({
396
+ 'time': time_str,
397
+ 'temp': item['main']['temp'],
398
+ 'feels_like': item['main']['feels_like'],
399
+ 'humidity': item['main']['humidity'],
400
+ 'pressure': item['main']['pressure'],
401
+ 'wind_speed': item['wind']['speed'],
402
+ 'wind_deg': item['wind']['deg'],
403
+ 'condition': item['weather'][0]['description'],
404
+ 'visibility': item.get('visibility', 'N/A'), # sometimes visibility may be missing
405
+ })
 
 
 
 
406
 
407
  # Process data to create daily summaries
408
  daily_summaries = {}
409
  for date_str, forecasts in forecast_data.items():
410
+ temps = [f['temp'] for f in forecasts]
411
+ feels_likes = [f['feels_like'] for f in forecasts]
412
+ humidities = [f['humidity'] for f in forecasts]
413
+ pressures = [f['pressure'] for f in forecasts]
414
+ wind_speeds = [f['wind_speed'] for f in forecasts]
415
+ conditions = [f['condition'] for f in forecasts]
416
 
417
  min_temp = min(temps)
418
  max_temp = max(temps)
 
427
  most_common_condition = condition_counts.most_common(1)[0][0]
428
 
429
  daily_summaries[date_str] = {
430
+ 'min_temp': min_temp,
431
+ 'max_temp': max_temp,
432
+ 'avg_temp': avg_temp,
433
+ 'avg_feels_like': avg_feels_like,
434
+ 'avg_humidity': avg_humidity,
435
+ 'avg_pressure': avg_pressure,
436
+ 'avg_wind_speed': avg_wind_speed,
437
+ 'condition': most_common_condition,
438
  }
439
 
440
  # Build the formatted string
441
+ city_name = weather_data['city']['name']
442
  ret_str = f"**5-Day Weather Forecast for {city_name}:**\n"
443
 
444
  for date_str in sorted(daily_summaries.keys()):
 
455
  return ret_str
456
 
457
 
458
+ llmmodel = OpenAI(api_token=os.environ["OPENAI_API_KEY"], model='gpt-4o')
459
 
460
  # Load dataframes
461
  dfcleaned = pd.read_csv("dfcleaned.csv")
462
+ dfcleaned['Timestamp'] = pd.to_datetime(dfcleaned['Timestamp'])
463
+ dfcleaned['off-nominal'] = dfcleaned['off-nominal'].apply(str)
464
  dfshaps = pd.read_csv("shaps.csv")
465
+ dfshaps['Timestamp'] = pd.to_datetime(dfshaps['Timestamp'])
466
 
467
  # Initialize Agent
468
  agent = Agent([dfcleaned, dfshaps], config={"llm": llmmodel})
 
471
  sdfcleaned = SmartDataframe(dfcleaned, config={"llm": llmmodel})
472
 
473
 
474
+
475
  def process_query(query):
476
  response = agent.chat(query) # Replace with your actual agent chat implementation
477
  print(response)
478
+
479
  # Initialize outputs and visibility flags
480
  text_output = None
481
  image_output = None
 
483
  text_visible = False
484
  image_visible = False
485
  dataframe_visible = False
486
+
487
  if isinstance(response, str) and ".png" not in response:
488
  text_output = response
489
  text_visible = True
490
  elif isinstance(response, str) and ".png" in response:
491
+ image_output = response # Assuming response is a filepath or URL to the image
492
+ image_visible = True
493
  elif isinstance(response, pd.DataFrame):
494
  dataframe_output = response
495
  dataframe_visible = True
496
+
497
  return (
498
  text_output,
499
  image_output,
500
  dataframe_output,
501
  gr.update(visible=text_visible),
502
  gr.update(visible=image_visible),
503
+ gr.update(visible=dataframe_visible)
504
  )
505
 
506
 
507
+
508
  def gradio_app():
509
  iface = gr.Interface(
510
  fn=process_query,
 
512
  outputs=[
513
  gr.Textbox(label="Response"),
514
  gr.Image(label="Plot"),
515
+ gr.DataFrame(label="Dataframe")
516
  ],
517
  title="pandasai Query Processor",
518
+ description="Enter your query related to the csv data files."
519
  )
520
  return iface
521
 
 
522
  with gr.Blocks(
523
+ # theme=gr.themes.Monochrome(primary_hue="green"),
524
+ theme = gr.themes.Soft(),
525
  ) as demo:
526
  with gr.Row(): # Combine the two weather functions into a single row
527
  with gr.Column():
528
+ location1 = gr.Textbox(label="Enter location for weather (e.g., Rio Rancho, New Mexico)",
529
+ value="Cambridge, Massachusetts")
 
 
530
  weather_button = gr.Button("Get Weather")
531
+ # output1 = gr.Markdown(label="Weather Information")
532
+ output1 = gr.Textbox(label="Weather Information", lines=8, max_lines=8, show_label=True, show_copy_button=True)
 
 
 
 
 
 
533
  weather_button.click(
534
  fn=update_weather,
535
  inputs=location1,
 
537
  api_name="update_weather",
538
  )
539
  with gr.Column():
540
+ location2 = gr.Textbox(label="Enter location for weather forecast (e.g., Rio Rancho, New Mexico)",
541
+ value="Cambridge, Massachusetts")
 
 
542
  weather_forecast_button = gr.Button("Get 5-Day Weather Forecast")
543
+ # output2 = gr.Markdown(label="Weather Forecast Information")
544
+ output2 = gr.Textbox(label="Weather 5-Day Forecast Information", lines=8, max_lines=8,
545
+ show_label=True, show_copy_button=True)
 
 
 
 
 
546
  weather_forecast_button.click(
547
  fn=update_weather_forecast,
548
  inputs=location2,
 
551
  )
552
  gr.Markdown("# 📄 PDF Viewer Section")
553
  gr.Markdown("Select a PDF from the dropdown below to view it.")
554
+
555
  with gr.Accordion("Open PDF Selection", open=False):
556
  with gr.Row():
557
  # Assign a larger scale to the dropdown
 
559
  choices=pdf_files,
560
  label="Select a PDF",
561
  value=DEFAULT_PDF, # Set a default value
562
+ scale=1 # This component takes twice the space
563
  )
564
  # Assign a smaller scale to the PDF viewer
565
  pdf_viewer = PDF(
566
  label="PDF Viewer",
567
  interactive=True,
568
+ scale=3 ,
569
+ value=display_pdf(DEFAULT_PDF)# This component takes half the space compared to dropdown
 
 
570
  )
571
+
572
  # Set up the event: when dropdown changes, update the PDF viewer
573
+ dropdown.change(
574
+ fn=display_pdf,
575
+ inputs=dropdown,
576
+ outputs=pdf_viewer
577
+ )
578
  with gr.Row():
579
  with gr.Column(scale=1):
580
  gr.Markdown("# Building Automation Assistant")
 
588
  # Update the ChatInterface to handle streaming
589
  chat_interface = gr.ChatInterface(
590
  chat,
591
+ #show_label=True,
592
+ # show_copy_button=True,
593
+ chatbot=gr.Chatbot(height=750, show_copy_button=True, show_copy_all_button=True,
594
+ avatar_images=("user_avatar.png", "assistant_avatar.png")),
 
 
 
 
595
  title="Ask Me Anything",
596
+ examples_per_page= 5,
597
+ # theme="soft", # glass
598
  description="Type your question about building automation here.",
599
  examples=[
600
  "Give the weather forecast for Cambridge, MA",
 
607
  "What are the most common maintenance challenges faced by net-zero energy buildings?",
608
  "How does the Uponor Climate Control Network System contribute to building maintenance?",
609
  "What role do smart systems play in the maintenance of sustainable buildings like HouseZero?",
610
+ "Can you provide data on the energy performance of HouseZero over the past year?",
611
+ "Tell me about the HouseZero dataset. Retrieve information from the publication you have access to. Use your file retrieval tool.",
612
  "Describe in detail the relationshp between the columns and values in the uploaded CSV files and the information you have access to regarding the HouseZero dataset. Be verbose. Use your file retrieval tool.",
613
  "Please comment on the zone relative humidity features, specifically if they indicate a problem withthe building",
614
  "Give me in great detail any advice you have to maintain a small to midsize office building, like the HouseZero data corresponds to. Be verbose. Use your file retrieval tool.",
615
  "Is there any information in the datafiles that indicates a problem with the building?",
616
  "Show Massachusetts electricity billing rates during the same time span as the CSV data",
617
  "Use those rates and the relevant columns in the CSV files to estimate how much it costs to operate this building per month.",
618
+ "What is the estimated average electricity cost for operating the building using massachusetts energy rates. use your file retrieval tool. use data csv files for building data. Limit your response to 140 characters. Use your file retrieval tool.",
619
  "Based on the data in these CSV files, can you assign an EnergyIQ score from 1-10 that reflects how well the building is operating? Explain the reason for your score and provide any recommendations on actions to take that can improve it in the future. Be verbose. Use your file retrieval tool.",
620
  "Please summarize information concerning sensor networks that may be leading to faulty meaurements.",
621
  "Tell me how to properly install the PVC sky lights.",
622
  "Based on data and insights, what specific changes should be made to HouseZero's maintenance protocols?",
623
+ "what recommendations do you have to mitigate against high relative humidity zone measurements in structures like the housezero building?"
624
  ],
625
  fill_height=True,
626
  )
 
631
  # with gr.Column():
632
  # Define the three ScatterPlot components
633
  anomaly_plot = gr.ScatterPlot(
634
+ dfcleaned,
635
+ x="Timestamp",
636
+ y="Z5_RH",
637
  color="off-nominal",
638
+ title="Anomaly Score"
639
  )
640
+
641
  zone3_plot = gr.ScatterPlot(
642
  dfcleaned,
643
  x="Timestamp",
 
653
  color="off-nominal",
654
  title="Zone 4 Relative Humidity",
655
  )
656
+
657
+ # Group all plots into a list for easy management
658
  plots = [anomaly_plot, zone3_plot, zone4_plot]
659
 
660
  def select_region(selection: gr.SelectData):
 
669
  """
670
  if selection is None or selection.index is None:
671
  return [gr.Plot.update() for _ in plots]
672
+
673
  min_x, max_x = selection.index
674
+ # Update the x_lim for each plot
675
  return [gr.ScatterPlot(x_lim=(min_x, max_x)) for _ in plots]
676
 
677
  def reset_region():
 
683
  """
684
  return [gr.ScatterPlot(x_lim=None) for _ in plots]
685
 
686
+ # Attach event listeners to each plot
687
  for plot in plots:
688
  plot.select(
689
+ select_region,
690
+ inputs=None,
691
+ outputs=plots # Update all plots
692
  )
693
  plot.double_click(
694
+ reset_region,
695
+ inputs=None,
696
+ outputs=plots # Reset all plots
697
  )
698
 
699
+ # plots = [plt, first_plot, second_plot]
 
 
 
 
700
 
701
+ # def select_region(selection: gr.SelectData):
702
+ # min_w, max_w = selection.index
703
+ # return gr.ScatterPlot(x_lim=(min_w, max_w))
704
 
705
+ # for p in plots:
706
+ # p.select(select_region, None, plots)
707
+ # p.double_click(lambda: [gr.LinePlot(x_lim=None)] * len(plots), None, plots)
708
+
709
+ # second_plot.select(select_second_region, None, plt)
710
+ # second_plot.double_click(lambda: gr.ScatterPlot(x_lim=None), None, plt)
711
+ # gr.Column([anomaly_plot, first_plot, second_plot])
712
 
713
+ # anomaly_info = gr.Markdown("Anomaly detected around October 15, 2023")
714
  with gr.Column():
715
+ query = gr.Textbox(label="Enter your question about the data",
716
+ value="Plot the anomaly_score as a function of time and highlight the highest 20 values")
 
 
717
  query_button = gr.Button("Submit Data Query")
718
  with gr.Row():
719
  with gr.Column(visible=False) as output_col1:
 
726
  fn=process_query,
727
  inputs=query,
728
  outputs=[
729
+ out1, # Text output
730
+ out2, # Image output
731
+ out3, # DataFrame output
732
+ output_col1, # Visibility for Text output
733
+ output_col2, # Visibility for Image output
734
+ output_col3 # Visibility for DataFrame output
735
  ],
736
+ api_name="process_query"
737
  )
738
+
739
  # hide visibility until its ready
740
+
741
+
742
  # Weather input
743
+ # with gr.Row():
744
+ # iface = gradio_app()
745
 
746
 
747
+ demo.launch(share=False)