plebias's picture
Update app.py
e6fa7ee verified
raw
history blame contribute delete
No virus
38.4 kB
# -*- coding: utf-8 -*-
# Default dependencies to run
import os
# from dotenv import load_dotenv
# load_dotenv()
import logging
import logging
import os
import re
import requests
import math
import time
import folium
import base64
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
from tqdm import tqdm
from datetime import datetime
from geopy.geocoders import Nominatim
import openai
from openai import OpenAI, AsyncOpenAI
import langchain_community.embeddings.huggingface
from langchain_community.embeddings.huggingface import HuggingFaceBgeEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.docstore.document import Document
#from dotenv import load_dotenv
#load_dotenv()
# Set environment variables
# os.environ['OPENAI_API_KEY'] = 'openaikeyhere'
# os.environ['DATAMALL_API_KEY'] = 'datamallkeyhere'
## new async
import asyncio
import aiohttp
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
if not OPENAI_API_KEY:
raise Exception("No OpenAI API Key found!")
client = OpenAI(api_key=OPENAI_API_KEY)
a_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
import logging
# import docx
import os
import re
import requests
import math
import time
import base64
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
from tqdm import tqdm
from datetime import datetime
from geopy.geocoders import Nominatim
import folium
import openai
from openai import OpenAI
import langchain_community.embeddings.huggingface
from langchain_community.embeddings.huggingface import HuggingFaceBgeEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.docstore.document import Document
from gradio_folium import Folium
import gradio as gr
# from dotenv import load_dotenv
# load_dotenv()
########################## Initialise API keys ##############################
#OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
#if not OPENAI_API_KEY:
# raise Exception("No OpenAI API Key found!")
DATAMALL_API_KEY = os.environ.get("DATAMALL_API_KEY")
if not DATAMALL_API_KEY:
raise Exception("No Datamall API Key found!")
########################## init base variables ##############################
## vector stores
model_name = "bge-large-en-v1.5"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
bge = HuggingFaceBgeEmbeddings(
model_name=model_name,
model_kwargs = model_kwargs,
encode_kwargs = encode_kwargs)
store_dict = {}
def get_store(index_name, embeddings = bge, rerun = False):
if not store_dict.get(index_name, None) or rerun:
store_dict[index_name] = FAISS.load_local(index_name, embeddings, allow_dangerous_deserialization=True)
return store_dict[index_name]
# synchronous
for x in ["SCDF", "LTA", "Traffic Police"]:
get_store(f"index/{x}").as_retriever(search_type="similarity", search_kwargs={"k":3})
########################## Audio to Summary functions ##############################
client = OpenAI(api_key=OPENAI_API_KEY)
a_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
def get_transcript_from_audio(audio_file_path):
"""
Provides transcript from audio file.
"""
with open(audio_file_path, "rb") as f:
transcript = client.audio.translations.create(
model="whisper-1",
file=f
)
return transcript.text
def get_summary_from_transcript(transcript):
"""
Provides summary with 5W1H from transcripted text.
"""
if type(transcript) == openai.types.audio.translation.Translation:
transcript = transcript.text
if type(transcript) is not str:
raise Exception(f"Wrong type of transcript. Expected type str, got type {type(transcript)}")
prompt = f"""You are provided with the following audio transcript of one or more calls between incident reporters and emergency responders in Singapore.
Provide a concise and detailed summary (1) based on the transcript. Road names in the transcript may be wrong and should be edited to reflect real roads in Singapore.
Separately provide the following information (2) with labels in [] strictly following the format {{[label]: info}} (3) based on the generated audio transcript (2) [!important: Do not include details not found in the audio transcript]:
[who], [what], ([where, direction]), closest single [landmark] phrase given by reporter, closest !single [road] phrase given by reporter, [when], [why], and [how] strictly based on the generated transcript. Example: {{[landmark]: landmark_name, \n [road]: road_name}}
\n\n----------------\n\nTranscript:\n{transcript}\n\n(1)\n\n"""
completion = client.completions.create(
model="gpt-3.5-turbo-instruct",
max_tokens=1000,
prompt=prompt,
temperature=0
)
summary = completion.choices[0].text
return summary
########################## Summary to Location retrieval (fire station, images, hospitals) functions ##############################
def extract_location_for_prompt(summary):
"""
Provides location for GPT prompt
"""
try:
location_pattern = r'\[where, direction]:\s*(.*?)\n'
location = re.search(location_pattern, summary).group(1)
# Split the string by commas
location_list = location.split(",")
# Trim whitespace from each element and append to a list
location_list = [item.strip() for item in location_list]
except:
location_list = extract_location_from_summary(summary)
return location_list
def extract_location_from_summary(summary):
"""
Provides a list of places identified from Summary + 5W1H
"""
try:
landmark_pattern = r'\[landmark\]:\s*(.*?)\n'
landmark = re.search(landmark_pattern, summary).group(1)
# Split the string by commas
if " and " in landmark:
landmark= landmark.replace(" and ", ',')
if "N/A" in landmark:
landmark= landmark.replace("N/A", ',')
landmark_list = landmark.split(",")
# Trim whitespace from each element and append to a list
landmark_list = [item.strip() for item in landmark_list if item.strip()]
except:
landmark_list = []
try:
road_pattern = r'\[road\]:\s*(.*?)\n'
road = re.search(road_pattern, summary).group(1)
print(road)
# Split the string by commas
if " and " in road:
road= road.replace(" and ", ',')
if "N/A" in road:
road= road.replace("N/A", ',')
road_list = road.split(",")
# Trim whitespace from each element and append to a list
road_list = [item.strip() for item in road_list if item.strip()]
except:
road_list = []
return landmark_list + road_list
def get_latlong(location_list):
"""
Approximates the location based on a list of places.
"""
geolocator = Nominatim(user_agent="user_agent")
lat_list, lng_list = [], []
for location in location_list:
try:
identified_location = geolocator.geocode(f"{location}, Singapore")
print(f"- Identified '{identified_location.address}' from '{location}'")
lat_list.append(identified_location.latitude)
lng_list.append(identified_location.longitude)
except:
print(f"- Unable to identify '{location}'")
return np.mean(lat_list), np.mean(lng_list)
def get_latlong_from_summary(summary):
"""
Gets the approximated location of the incident based on Summary + 5W1H
"""
# Get a list of locations from the summary
location_list = extract_location_from_summary(summary)
print(f"\nLocations identified: {location_list}")
# Get approximated location of the incident
lat, lng = get_latlong(location_list)
print(f"Estimated lat, lng: ({lat}, {lng})\n")
return lat, lng
def call_api(api_url):
"""
Makes Datamall API request
"""
# Make sure to add any necessary headers for your API request here
headers = {
'AccountKey': DATAMALL_API_KEY,
'accept': 'application/json' # Example header, adjust as necessary
}
# Call the API
response = requests.get(api_url, headers=headers)
# Check if the response was successful
if response.status_code == 200:
# Parse the JSON response
data = response.json()
# Extracting the list of incidents from the 'value' key
df = pd.DataFrame(data['value'])
else:
print("Failed to retrieve data. Status code:", response.status_code)
return df
# Function to calculate distance using Haversine formula
def haversine(lat1, lon1, lat2, lon2):
"""
Calculates the distance between 2 entities.
"""
# Radius of the Earth in km
R = 6371.0
# Convert latitude and longitude from degrees to radians
lat1 = np.radians(lat1)
lon1 = np.radians(lon1)
lat2 = np.radians(lat2)
lon2 = np.radians(lon2)
# Calculate the change in coordinates
dlat = lat2 - lat1
dlon = lon2 - lon1
# Haversine formula
a = np.sin(dlat / 2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2)**2
c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
# Distance
distance = R * c
return distance
def encode_image_to_base64(response):
"""
Encodes HTTP request and decodes it as a UTF-8 encoded string.
"""
encoded_string = base64.b64encode(response.content).decode('utf-8')
return encoded_string
def decode_base64_to_image(encoded_string):
"""
Decodes an encoded string into binary data.
"""
return base64.b64decode(encoded_string)
def get_nearest_camera(latlong, num):
"""
Retrieve the information of "num" nearest Traffic Cameras based on specified lat, lng
"""
if not num:
return
lat, lng = latlong
# Extract camera location and imagelink via Datamall API
cameraimg_df = call_api('http://datamall2.mytransport.sg/ltaodataservice/Traffic-Imagesv2?long=')
cameraimg_df['CameraID'] = cameraimg_df['CameraID'].astype('int64')
# Extract additional camera information from database
camerainfo_df = pd.read_csv("data/traffic_images.csv")
# Update cameraimg_df
merged_df = pd.merge(cameraimg_df, camerainfo_df[["CameraID", "Description", "Section"]], on='CameraID', how='inner')
cameraimg_df = merged_df
# Calculate distances
cameraimg_df['Distance'] = haversine(lat,lng, cameraimg_df['Latitude'], cameraimg_df['Longitude'])
closest_cam = cameraimg_df.sort_values(by='Distance').head(num)
# Append encoded image and time retrieved into dataframe
img_list, camera_coords, encoded_img, datetime_list = [], [], [], []
current_time = datetime.now().strftime('%d/%m/%Y %I:%M:%S:%f %p')
for idx in closest_cam.index:
response = requests.get(closest_cam["ImageLink"][idx])
img_list.append(closest_cam["ImageLink"][idx])
encoded_img.append(encode_image_to_base64(response))
print('time after embed image:', datetime.now().strftime('%I:%M:%S:%f %p'))
datetime_list.append(current_time)
closest_cam["encoded_img"] = encoded_img
closest_cam["time_retrieved"] = datetime_list
return closest_cam, cameraimg_df
async def a_get_nearest_camera(latlong, num):
"""
Retrieve the information of "num" nearest Traffic Cameras based on specified lat, lng
"""
if not num:
return
lat, lng = latlong
cameraimg_df = call_api('http://datamall2.mytransport.sg/ltaodataservice/Traffic-Imagesv2?long=')
cameraimg_df['CameraID'] = cameraimg_df['CameraID'].astype('int64')
camerainfo_df = pd.read_csv("data/traffic_images.csv")
merged_df = pd.merge(cameraimg_df, camerainfo_df[["CameraID", "Description", "Section"]], on='CameraID', how='inner')
cameraimg_df = merged_df
# Calculate distances
cameraimg_df['Distance'] = haversine(lat,lng, cameraimg_df['Latitude'], cameraimg_df['Longitude'])
closest_cam = cameraimg_df.sort_values(by='Distance').head(num)
# Append encoded image and time retrieved into dataframe
img_list, camera_coords, encoded_img, datetime_list = [], [], [], []
current_time = datetime.now().strftime('%d/%m/%Y %I:%M:%S:%f %p')
async def fetch_image_base64(session, url):
async with session.get(url) as response:
res = await response.read()
return base64.b64encode(res).decode('utf-8')
async with aiohttp.ClientSession() as session:
tasks = []
for idx in closest_cam.index:
task = fetch_image_base64(session, closest_cam["ImageLink"][idx])
tasks.append(task)
img_list.append(closest_cam["ImageLink"][idx])
datetime_list.append(current_time)
encoded_img = await asyncio.gather(*tasks)
closest_cam["encoded_img"] = encoded_img
closest_cam["time_retrieved"] = datetime_list
return closest_cam, cameraimg_df
def get_firestation_from_latlong(latlong, num):
"""
Retrieves the "num" nearest firestation based on specified lat, lng
"""
if not num:
return
lat,lng = latlong
civil_df = pd.read_excel("data/fire_hosp.xlsx")
civil_df = civil_df[civil_df["category"].isin(["Firestation", "Firepost"])]
# Calculate distances
civil_df['Distance'] = haversine(lat,lng, civil_df['lat'], civil_df['long'])
closest_fire = civil_df.sort_values(by='Distance').head(num)
return closest_fire
def get_hospital_from_latlong(latlong, num):
"""
Retrieves the "num" nearest firestation based on specified lat, lng
"""
if not num:
return
lat,lng = latlong
civil_df = pd.read_excel("data/fire_hosp.xlsx")
civil_df = civil_df[civil_df["category"].isin(["Hospital"])]
# Calculate distances
civil_df['Distance'] = haversine(lat,lng, civil_df['lat'], civil_df['long'])
closest_hosp = civil_df.sort_values(by='Distance').head(num)
return closest_hosp
########################## Location to Map generator functions ##############################
def get_map_from_summary(summary_txt):
"""
Provide a Folium Map showing the location of the incident and the "num" nearest traffic
cameras, fire stations and ambulance sites.
"""
lat, lng = get_latlong_from_summary(summary_txt)
if pd.isna(lat) and pd.isna(lng):
print("Lat, Lng cannot be determined. Please try again")
return None
else:
# cameraimg_df = call_api('http://datamall2.mytransport.sg/ltaodataservice/Traffic-Imagesv2?long=')
# print('nearest cam')
nearest_cam_df, cameraimg_df= get_nearest_camera((lat,lng), 3)
# print('ok. nearest fire')
nearest_fire_df = get_firestation_from_latlong((lat,lng), 1)
# print('ok. nearest hosp')
nearest_hosp_df = get_hospital_from_latlong((lat,lng), 1)
avg_lat = np.mean(cameraimg_df["Latitude"])
avg_lng = np.mean(cameraimg_df["Longitude"])
# print('ok. folium map')
map = folium.Map(location=[avg_lat, avg_lng], zoom_start=12)
fg = folium.FeatureGroup().add_to(map)
folium.Marker(location=[float(lat), float(lng)],
icon=folium.Icon(color='red'),
popup="Incident"
).add_to(fg)
for idx in tqdm(cameraimg_df.index, desc="Processing Traffic Cameras"):
if cameraimg_df["CameraID"][idx] in list(nearest_cam_df["CameraID"]):
print('added nearby camera', nearest_cam_df["Description"][idx])
html = '<h3 style="display:inline;">{}</h3><div>{}</div><img style="width:320; height:240;" src="data:image/jpeg;base64,{}">'.format(
nearest_cam_df["Description"][idx],
nearest_cam_df["time_retrieved"][idx],
nearest_cam_df["encoded_img"][idx]
)
iframe = folium.IFrame(html, width=320+40, height=240+60)
popup = folium.Popup(iframe, max_height=350)
folium.Marker(location=[nearest_cam_df["Latitude"][idx], nearest_cam_df["Longitude"][idx]],
icon=folium.Icon(color='blue'),
popup=popup).add_to(fg)
else:
# Add marker for the camera with the specified color
folium.Marker(location=[cameraimg_df["Latitude"][idx], cameraimg_df["Longitude"][idx]], icon=folium.Icon(color='gray')).add_to(map)
for idx in tqdm(nearest_fire_df.index, desc="Processing Fire Stations"):
folium.Marker(location=[nearest_fire_df["lat"][idx], nearest_fire_df["long"][idx]],
icon=folium.Icon(color='orange'),
popup=nearest_fire_df["name"][idx]).add_to(fg)
for idx in tqdm(nearest_hosp_df.index, desc="Processing Hospitals"):
folium.Marker(location=[nearest_hosp_df["lat"][idx], nearest_hosp_df["long"][idx]],
icon=folium.Icon(color='green'),
popup=nearest_hosp_df["name"][idx]).add_to(fg)
map.fit_bounds(fg.get_bounds(), padding=(30, 30))
return map
async def a_get_map_from_summary(summary_txt, get_num_cameras=3):
"""
Provide a Folium Map showing the location of the incident and the "num" nearest traffic
cameras, fire stations and ambulance sites.
"""
lat, lng = get_latlong_from_summary(summary_txt)
if pd.isna(lat) and pd.isna(lng):
print("Lat, Lng cannot be determined. Please try again")
return None
else:
# cameraimg_df = call_api('http://datamall2.mytransport.sg/ltaodataservice/Traffic-Imagesv2?long=')
nearest_cam_df, cameraimg_df= await a_get_nearest_camera((lat,lng), get_num_cameras)
nearest_fire_df = get_firestation_from_latlong((lat,lng), 1)
nearest_hosp_df = get_hospital_from_latlong((lat,lng), 1)
avg_lat = np.mean(cameraimg_df["Latitude"])
avg_lng = np.mean(cameraimg_df["Longitude"])
map = folium.Map(location=[avg_lat, avg_lng], zoom_start=12)
fg = folium.FeatureGroup().add_to(map)
folium.Marker(location=[float(lat), float(lng)],
icon=folium.Icon(color='red'),
popup="Incident"
).add_to(fg)
for idx in tqdm(cameraimg_df.index, desc="Processing Traffic Cameras"):
if cameraimg_df["CameraID"][idx] in list(nearest_cam_df["CameraID"]):
print('added nearby camera', nearest_cam_df["Description"][idx])
html = '<h3 style="display:inline;">{}</h3><div>{}</div><img style="width:320; height:240;" src="data:image/jpeg;base64,{}">'.format(
nearest_cam_df["Description"][idx],
nearest_cam_df["time_retrieved"][idx],
nearest_cam_df["encoded_img"][idx]
)
iframe = folium.IFrame(html, width=320+40, height=240+60)
popup = folium.Popup(iframe, max_height=350)
folium.Marker(location=[nearest_cam_df["Latitude"][idx], nearest_cam_df["Longitude"][idx]],
icon=folium.Icon(color='blue'),
popup=popup).add_to(fg)
else:
# Add marker for the camera with the specified color
folium.Marker(location=[cameraimg_df["Latitude"][idx], cameraimg_df["Longitude"][idx]], icon=folium.Icon(color='gray')).add_to(map)
for idx in tqdm(nearest_fire_df.index, desc="Processing Fire Stations"):
folium.Marker(location=[nearest_fire_df["lat"][idx], nearest_fire_df["long"][idx]],
icon=folium.Icon(color='orange'),
popup=nearest_fire_df["name"][idx]).add_to(fg)
for idx in tqdm(nearest_hosp_df.index, desc="Processing Hospitals"):
folium.Marker(location=[nearest_hosp_df["lat"][idx], nearest_hosp_df["long"][idx]],
icon=folium.Icon(color='green'),
popup=nearest_hosp_df["name"][idx]).add_to(fg)
map.fit_bounds(fg.get_bounds(), padding=(30, 30))
return map
########################## RAG to Recommendations functions ##############################
action_prompt = """\
**Traffic Incident Response Assistant**
You are a dispatching agent for traffic conditions. You will be provided with the Standard Operating Procedures (SOPs) of various departments, with a description of what each department's roles and responsibilities are. From these information, you are a well-versed dispatcher that can recommend the corresponding actions accurately for various scenarios.
**Your Task**
Your task is to analyze the provided information and generate a short and sweet summary for the stakeholder: {stakeholder}. This summary should extract key steps from the relevant SOPs, tailored to the specific incident scenario.
**Information Provided:**
You will be provided the following information:
1. **Roles and Responsibilities**: The stakeholder and its roles and responsibilities.
2. **Incident Report Summary**: A concise description of the incident location and nature.
3. **Standard Operating Procedures**: A list of relevant sections from various SOPs for {stakeholder}.
----------------------------------------------------------------
**Roles and Responsibilities**
Here is the description of the stakeholder, and a description of its roles and responsibilities.
Stakeholder: {stakeholder}
**Roles and Responsibilities**:
{stakeholder_role}
----------------------------------------------------------------
Below is the incident summary, and location of the incident.
Location: {location}
**Incident Summary**:
{summary}
----------------------------------------------------------------
Below is some relevant standard operating procedures.
You will be provided with a list of SOPs, that are possibly relevant to the incident. They will be split with ==============.
The filename of the document and the contents will be provided below.
{ref_content}
----------------------------------------------------------------
Given the situation above and the relevant SOPs, provide in detail the relevant procedure recommendations for the stakeholder {stakeholder}.
**Important**
* Remember to keep the action plan concise short and sweet. Incorporate only the necessary action plans from the relevant SOPs.
**Your Response**:
"""
stakeholder_roles_gpt35 = {
"SCDF": "The Singapore Civil Defence Force (SCDF) plays a crucial role in managing traffic incidents, including accidents, vehicle breakdowns, and road blockages. Their responsibilities include providing emergency medical services, extrication of trapped individuals, and ensuring public safety during such incidents. \n\nThe SCDF is mandated to respond to emergencies and protect lives and property. Traffic incidents often involve casualties and pose risks to public safety. SCDF's expertise in emergency medical services and rescue operations enables them to provide timely assistance, including medical care, extrication of trapped individuals, and clearing obstructions to restore traffic flow swiftly. Their swift response helps minimize casualties, alleviate traffic congestion, and ensure smooth coordination with other agencies for effective incident management.",
"LTA": "The Land Transport Authority (LTA) in Singapore is responsible for managing and regulating various aspects of the transportation system, including responding to traffic incidents. Their roles involve coordinating with other agencies, managing traffic flow, implementing road safety measures, and providing real-time information to the public during incidents. \n\nLTA is tasked with ensuring smooth and safe transportation operations. During traffic incidents, LTA's role becomes crucial in managing traffic flow, implementing diversions, and coordinating with relevant agencies to clear obstructions promptly. They leverage technology and infrastructure such as traffic lights, CCTV cameras, and electronic signages to monitor and manage traffic effectively. Additionally, LTA disseminates real-time updates to the public to facilitate informed decision-making and minimize disruptions caused by incidents.",
"Traffic Police": "The Traffic Police in Singapore are tasked with managing traffic incidents, including accidents, road obstructions, and heavy traffic. Their responsibilities involve ensuring road safety, managing traffic flow, conducting investigations, and enforcing traffic laws to prevent further incidents and maintain order on the roads. \n\nTraffic Police are essential for maintaining order and safety on Singapore's roads. When incidents occur, they must promptly respond to manage traffic, ensure the safety of motorists and pedestrians, and investigate the causes to prevent recurrence. Their enforcement of traffic laws deters reckless behavior and promotes compliance, contributing to overall road safety. Through effective coordination with other agencies, Traffic Police play a vital role in minimizing disruptions and ensuring smooth traffic flow during incidents."
}
stakeholder_roles = stakeholder_roles_gpt35
def retrieve_stakeholder_sop_from_summary(stakeholder, summary_txt, top_k = 3):
# print('getting sop for', stakeholder)
retriever = get_store(f"index/{stakeholder}").as_retriever(search_type="similarity", search_kwargs={"k":top_k})
selected_sops = retriever.invoke(summary_txt)
ref_content = [sop.page_content for sop in selected_sops]
ref_filename = [str(sop.metadata) for sop in selected_sops]
# print('retrieved sop for', stakeholder)
return (ref_content, ref_filename)
def retrieve_sop_from_summary(summary_txt,
stakeholders = ["SCDF", "LTA", "Traffic Police"],
top_k = 3
):
sops_retrieved = {}
for stakeholder in stakeholders:
sops_retrieved[stakeholder] = retrieve_stakeholder_sop_from_summary(stakeholder, summary_txt, top_k)
return sops_retrieved
def get_actions_from_summary(summary_txt, location = None,
stakeholders = ["SCDF", "LTA", "Traffic Police"],
top_k = 3):
"""
Provides a json output of the SOPs for the relevant stakeholders based on the Summary + 5W1H
processed from the transcript.
"""
sops_retrieved = retrieve_sop_from_summary(summary_txt, top_k = top_k)
results = {}
for stakeholder in stakeholders:
ref_content, ref_filename = sops_retrieved[stakeholder]
stakeholder_action_prompt = action_prompt.format(
summary=summary_txt,
location=location,
# ref_content=ref_content,
# ref_filename=ref_filename,
ref_content = ("\n"+'='*20+"\n").join(f"{i}" for i, j in zip(ref_content,ref_filename)),
stakeholder=stakeholder,
stakeholder_role = stakeholder_roles[stakeholder])
completion = client.chat.completions.create(
model = "gpt-3.5-turbo-1106",
temperature=0.,
max_tokens=1000,
messages=[{
"role": "system",
"content": stakeholder_action_prompt.format()}]
)
results[stakeholder] = ({
"stakeholder": stakeholder,
# "result_sop": completion.choices[0].text,
"actionables": completion.choices[0].message.content,
"ref_content": ref_content,
"ref_filename" : ref_filename,
# "images": images,
# "ambulance_needed": "1",
# "fire_truck_needed": "1",
})
return results
async def a_retrieve_stakeholder_sop_from_summary(stakeholder, summary_txt, top_k = 3):
# print('getting sop for', stakeholder)
retriever = get_store(f"index/{stakeholder}").as_retriever(search_type="similarity", search_kwargs={"k":top_k})
## async
selected_sops = await retriever.ainvoke(summary_txt)
ref_content = [sop.page_content for sop in selected_sops]
ref_filename = [str(sop.metadata) for sop in selected_sops]
# print('retrieved sop for', stakeholder)
return (ref_content, ref_filename)
async def a_retrieve_sop_from_summary(summary_txt,
stakeholders = ["SCDF", "LTA", "Traffic Police"],
top_k = 3
):
sops_retrieved = {}
tasks = []
async def run_tasks():
for stakeholder in stakeholders:
tasks.append(a_retrieve_stakeholder_sop_from_summary(stakeholder, summary_txt, top_k))
results = await asyncio.gather(*tasks)
return results
results = await run_tasks()
for stakeholder, result in zip(stakeholders, results):
sops_retrieved[stakeholder] = result
return sops_retrieved
async def a_get_actions_from_summary(summary_txt, location = None,
stakeholders = ["SCDF", "LTA", "Traffic Police"],
top_k = 3):
"""
Provides a json output of the SOPs for the relevant stakeholders based on the Summary + 5W1H
processed from the transcript.
"""
# sops_retrieved = await a_retrieve_sop_from_summary(summary_txt, top_k = top_k)
results = {}
for stakeholder in stakeholders:
# ref_content, ref_filename = sops_retrieved[stakeholder]
results[stakeholder] = ({
"stakeholder": stakeholder,
# "actionables": completion.choices[0].message.content,
# "ref_content": ref_content,
# "ref_filename" : ref_filename,
})
# for stakeholder in stakeholders:
async def a_get_stakeholder_actions_from_summary(stakeholder):
# ref_content, ref_filename = sops_retrieved[stakeholder]
ref_content, ref_filename = await a_retrieve_stakeholder_sop_from_summary(stakeholder, summary_txt, top_k)
stakeholder_action_prompt = action_prompt.format(
summary=summary_txt,
location=location,
# ref_content=ref_content,
# ref_filename=ref_filename,
ref_content = ("\n"+'='*20+"\n").join(f"{i}" for i, j in zip(ref_content,ref_filename)),
stakeholder=stakeholder,
stakeholder_role = stakeholder_roles[stakeholder])
completion = await a_client.chat.completions.create(
model = "gpt-3.5-turbo-1106",
temperature=0.,
max_tokens=1000,
messages=[{
"role": "system",
"content": stakeholder_action_prompt.format()}]
)
# print(stakeholder,"\n", completion.choices[0].text)
return ({
# "stakeholder": stakeholder,
# "result_sop": completion.choices[0].text,
"actionables": completion.choices[0].message.content,
"ref_content": ref_content,
"ref_filename" : ref_filename,
# "images": images,
# "ambulance_needed": "1",
# "fire_truck_needed": "1",
})
tasks = []
async def run_tasks():
for stakeholder in stakeholders:
tasks.append(a_get_stakeholder_actions_from_summary(stakeholder))
return await asyncio.gather(*tasks)
actions_results = await run_tasks()
for stakeholder, action_result in zip(stakeholders, actions_results):
results[stakeholder].update(action_result)
return results
########################## Final Output function ##############################
def disseminate_actions(smry):
"""
Provides relevant information and recommended actions based on the Summary + 5W1H processed
from the transcript.
"""
location = extract_location_for_prompt(smry)
actionables = get_actions_from_summary(smry, location)
folium_map = get_map_from_summary(smry)
return actionables, folium_map
# for action in actions:
# stakeholder = action.get('stakeholder')
# ## TODO: Dissmeniate based on where the stakeholder is supposed to be
async def a_disseminate_actions(smry):
location = extract_location_for_prompt(smry)
tasks = [a_get_actions_from_summary(smry, location), a_get_map_from_summary(smry)]
actionables, folium_map = await asyncio.gather(*tasks)
return actionables, folium_map
########################## gradio code ##############################
import gradio as gr
async def change_accordion(x):
# print("debug: accordion")
if len(x) >0 and x!= 'Summary':
isOpen = True
actionables, folium_map = await a_disseminate_actions(x)
if folium_map == None:
return (gr.Accordion("Recommendations output", visible=isOpen), gr.Textbox(visible=False) , \
folium.Map(location=[1.2879, 103.8517], zoom_start=12), \
actionables['SCDF']["actionables"], actionables['LTA']["actionables"], actionables['Traffic Police']["actionables"])
# return gr.Accordion("Image Location", open=isOpen), map_out
return (gr.Accordion("Recommendations output and Locations on Map", visible=isOpen), gr.Textbox(visible=False) ,folium_map, \
actionables['SCDF']["actionables"], actionables['LTA']["actionables"], actionables['Traffic Police']["actionables"])
else:
isOpen = False
# return gr.Accordion("Image Location", open=isOpen), x[1]
return (gr.Accordion("Recommendations output and Locations on Map", visible=isOpen), gr.Textbox(visible=False), \
folium.Map(location=[1.2879, 103.8517], zoom_start=12), recc_textbox1,recc_textbox2, recc_textbox3)
js = '''
function refresh() {
const url = new URL(window.location);
if (url.searchParams.get('__theme') !== 'dark') {
url.searchParams.set('__theme', 'dark');
window.location.href = url.href;
}
}
'''
#trans_textbox = gr.Textbox('Transcript',lines=10,max_lines=19, autoscroll = False, label = 'Edit road names as required (e.g. Lawney --> Lornie)', interactive = True)
trans_textbox = gr.Textbox('Transcript',lines=10,max_lines=19, autoscroll = False, interactive = False)
summ_textbox = gr.Textbox('Summary',lines=10,max_lines=19, autoscroll = False)
# recc_textbox1 = gr.Textbox(label="SCDF", max_lines=8, show_copy_button=True)
# recc_textbox2 = gr.Textbox(label="LTA", max_lines=8, show_copy_button=True)
# recc_textbox3 = gr.Textbox(label="Traffic Police", max_lines=8, show_copy_button=True)
# map_gr = Folium(value=map, height=400)
with gr.Blocks(js=js) as demo:
# summ_textbox = gr.Textbox('Summary')
gr.Markdown("Emergency Responder Copilot Demonstrator")
isOpen = False
outs_textbox = gr.Textbox("Recommendations outputs will appear here once summary is generated from audio transcript.\n",lines=1,max_lines=1, autoscroll = False, show_label= False)
with gr.Accordion("Recommendations output and Locations on Map", open=True, visible = isOpen) as img:
with gr.Row():
recc_textbox1 = gr.Textbox(label="SCDF", max_lines=10, show_copy_button=True, autoscroll = False)
recc_textbox2 = gr.Textbox(label="LTA", max_lines=10, show_copy_button=True, autoscroll = False)
recc_textbox3 = gr.Textbox(label="Traffic Police", max_lines=10, show_copy_button=True, autoscroll = False)
# gr.Markdown(loc)
map_gr = Folium(value=folium.Map(location=[1.2879, 103.8517], zoom_start=12), height=400)
with gr.Tab("Transcript from Audio"):
input_audio = gr.Audio(
sources=["microphone"],
type = 'filepath',
waveform_options=gr.WaveformOptions(
waveform_color="#01C6FF",
waveform_progress_color="#0066B4",
skip_length=2,
show_controls=False,
),
)
audio2trans = gr.Interface(
fn=get_transcript_from_audio,
inputs=input_audio,
outputs=trans_textbox,
examples=[\
# ],
# "audio/call_711.mp3", \
"audio/example1.m4a", "audio/example2.m4a"],
cache_examples=True,
allow_flagging = 'never'
)
with gr.Tab("Summarisation from Transcript") as tab2:
trans2summary = gr.Interface(
fn=get_summary_from_transcript,
inputs=trans_textbox,
outputs=summ_textbox,
allow_flagging = 'never'
)
# trans_textbox.change(lambda x: gr.Tab("Summarisation from Audio"), inputs=[trans_textbox], outputs=[tab2], scroll_to_output = True)
gr.Textbox("[1] You may click on one of the examples to load our existing examples. Alternatively, you may record your own incident for demonstration purposes using the Record button. Include any details about which junction/road the accident occured or nearby landmarks.\n[2] If you recorded your own incident, click Submit button to generate a transcript of the call.\n[3] Once the transcript has been generated and appears on the right side, click the Summarisation from Transcript tab.\n[4] Click submit to generate an Incident Summary Report. Once this is complete, please wait momentarily while recommendations for action are generated.", autoscroll = False, show_label= True, label='Instructions for use:')
summ_textbox.change(change_accordion, inputs=[summ_textbox], outputs=[img, outs_textbox, map_gr, recc_textbox1,recc_textbox2, recc_textbox3], scroll_to_output = True)
demo.launch(debug = True)