feat: Update skills package and add vehicle status functionality
Browse files- apis.py +0 -285
- car_assistant_eta.ipynb +175 -35
- skills/__init__.py +2 -1
- skills/common.py +1 -1
- skills/poi.py +143 -0
- skills/routing.py +5 -6
- skills/vehicle.py +1 -1
apis.py
CHANGED
@@ -1,5 +1,3 @@
|
|
1 |
-
import requests
|
2 |
-
|
3 |
from geopy.geocoders import Nominatim
|
4 |
|
5 |
|
@@ -53,287 +51,4 @@ def check_city_coordinates(lat = "", lon = "", city = "", **kwargs):
|
|
53 |
lon = coord.longitude
|
54 |
return lat, lon, city
|
55 |
|
56 |
-
# Select coordinates at equal distance, including the last one
|
57 |
-
def select_equally_spaced_coordinates(coords, number_of_points=10):
|
58 |
-
n = len(coords)
|
59 |
-
selected_coords = []
|
60 |
-
interval = max((n - 1) / (number_of_points - 1), 1)
|
61 |
-
for i in range(number_of_points):
|
62 |
-
# Calculate the index, ensuring it doesn't exceed the bounds of the list
|
63 |
-
index = int(round(i * interval))
|
64 |
-
if index < n:
|
65 |
-
selected_coords.append(coords[index])
|
66 |
-
return selected_coords
|
67 |
-
|
68 |
-
def find_points_of_interest(lat="0", lon="0", city="", type_of_poi="restaurant", **kwargs):
|
69 |
-
"""
|
70 |
-
Return some of the closest points of interest for a specific location and type of point of interest. The more parameters there are, the more precise.
|
71 |
-
:param lat (string): latitude
|
72 |
-
:param lon (string): longitude
|
73 |
-
:param city (string): Required. city
|
74 |
-
:param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
|
75 |
-
"""
|
76 |
-
lat, lon, city = check_city_coordinates(lat,lon,city)
|
77 |
-
|
78 |
-
r = requests.get(f'https://api.tomtom.com/search/2/search/{type_of_poi}'
|
79 |
-
'.json?key={0}&lat={1}&lon={2}&radius=10000&idxSet=POI&limit=100'.format(
|
80 |
-
TOMTOM_KEY,
|
81 |
-
lat,
|
82 |
-
lon
|
83 |
-
))
|
84 |
-
|
85 |
-
# Parse JSON from the response
|
86 |
-
data = r.json()
|
87 |
-
#print(data)
|
88 |
-
# Extract results
|
89 |
-
results = data['results']
|
90 |
-
|
91 |
-
# Sort the results based on distance
|
92 |
-
sorted_results = sorted(results, key=lambda x: x['dist'])
|
93 |
-
#print(sorted_results)
|
94 |
-
|
95 |
-
# Format and limit to top 5 results
|
96 |
-
formatted_results = [
|
97 |
-
f"The {type_of_poi} {result['poi']['name']} is {int(result['dist'])} meters away"
|
98 |
-
for result in sorted_results[:5]
|
99 |
-
]
|
100 |
-
|
101 |
-
|
102 |
-
return ". ".join(formatted_results)
|
103 |
-
|
104 |
-
def find_route(lat_depart="0", lon_depart="0", city_depart="", address_destination="", depart_time ="", **kwargs):
|
105 |
-
"""
|
106 |
-
Return the distance and the estimated time to go to a specific destination from the current place, at a specified depart time.
|
107 |
-
:param lat_depart (string): latitude of depart
|
108 |
-
:param lon_depart (string): longitude of depart
|
109 |
-
:param city_depart (string): Required. city of depart
|
110 |
-
:param address_destination (string): Required. The destination
|
111 |
-
:param depart_time (string): departure hour, in the format '08:00:20'.
|
112 |
-
"""
|
113 |
-
print(address_destination)
|
114 |
-
date = "2025-03-29T"
|
115 |
-
departure_time = '2024-02-01T' + depart_time
|
116 |
-
lat, lon, city = check_city_coordinates(lat_depart,lon_depart,city_depart)
|
117 |
-
lat_dest, lon_dest = find_coordinates(address_destination)
|
118 |
-
#print(lat_dest, lon_dest)
|
119 |
-
|
120 |
-
#print(departure_time)
|
121 |
-
|
122 |
-
r = requests.get('https://api.tomtom.com/routing/1/calculateRoute/{0},{1}:{2},{3}/json?key={4}&departAt={5}'.format(
|
123 |
-
lat_depart,
|
124 |
-
lon_depart,
|
125 |
-
lat_dest,
|
126 |
-
lon_dest,
|
127 |
-
TOMTOM_KEY,
|
128 |
-
departure_time
|
129 |
-
))
|
130 |
-
|
131 |
-
# Parse JSON from the response
|
132 |
-
data = r.json()
|
133 |
-
#print(data)
|
134 |
-
|
135 |
-
#print(data)
|
136 |
-
|
137 |
-
result = data['routes'][0]['summary']
|
138 |
-
|
139 |
-
# Calculate distance in kilometers (1 meter = 0.001 kilometers)
|
140 |
-
distance_km = result['lengthInMeters'] * 0.001
|
141 |
-
|
142 |
-
# Calculate travel time in minutes (1 second = 1/60 minutes)
|
143 |
-
time_minutes = result['travelTimeInSeconds'] / 60
|
144 |
-
if time_minutes < 60:
|
145 |
-
time_display = f"{time_minutes:.0f} minutes"
|
146 |
-
else:
|
147 |
-
hours = int(time_minutes / 60)
|
148 |
-
minutes = int(time_minutes % 60)
|
149 |
-
time_display = f"{hours} hours" + (f" and {minutes} minutes" if minutes > 0 else "")
|
150 |
-
|
151 |
-
# Extract arrival time from the JSON structure
|
152 |
-
arrival_time_str = result['arrivalTime']
|
153 |
-
|
154 |
-
# Convert string to datetime object
|
155 |
-
arrival_time = datetime.fromisoformat(arrival_time_str)
|
156 |
-
|
157 |
-
# Extract and display the arrival hour in HH:MM format
|
158 |
-
arrival_hour_display = arrival_time.strftime("%H:%M")
|
159 |
-
|
160 |
-
|
161 |
-
# return the distance and time
|
162 |
-
return(f"The route to go to {address_destination} is {distance_km:.2f} km and {time_display}. Leaving now, the arrival time is estimated at {arrival_hour_display} " )
|
163 |
-
|
164 |
-
|
165 |
-
# Sort the results based on distance
|
166 |
-
#sorted_results = sorted(results, key=lambda x: x['dist'])
|
167 |
-
|
168 |
-
#return ". ".join(formatted_results)
|
169 |
-
|
170 |
-
|
171 |
-
def search_along_route(latitude_depart, longitude_depart, city_destination, type_of_poi):
|
172 |
-
"""
|
173 |
-
Return some of the closest points of interest along the route from the depart point, specified by its coordinates and a city destination.
|
174 |
-
:param latitude_depart (string): Required. Latitude of depart location
|
175 |
-
:param longitude_depart (string): Required. Longitude of depart location
|
176 |
-
:param city_destination (string): Required. City destination
|
177 |
-
:param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
|
178 |
-
"""
|
179 |
-
|
180 |
-
lat_dest, lon_dest = find_coordinates(city_destination)
|
181 |
-
print(lat_dest)
|
182 |
-
|
183 |
-
r = requests.get('https://api.tomtom.com/routing/1/calculateRoute/{0},{1}:{2},{3}/json?key={4}'.format(
|
184 |
-
latitude_depart,
|
185 |
-
longitude_depart,
|
186 |
-
lat_dest,
|
187 |
-
lon_dest,
|
188 |
-
TOMTOM_KEY
|
189 |
-
))
|
190 |
-
|
191 |
-
coord_route = select_equally_spaced_coordinates(r.json()['routes'][0]['legs'][0]['points'])
|
192 |
-
|
193 |
-
# The API endpoint for searching along a route
|
194 |
-
url = f'https://api.tomtom.com/search/2/searchAlongRoute/{type_of_poi}.json?key={TOMTOM_KEY}&maxDetourTime=700&limit=20&sortBy=detourTime'
|
195 |
-
|
196 |
-
# The data payload
|
197 |
-
payload = {
|
198 |
-
"route": {
|
199 |
-
"points": [
|
200 |
-
{"lat": float(latitude_depart), "lon": float(longitude_depart)},
|
201 |
-
{"lat": float(coord_route[1]['latitude']), "lon": float(coord_route[1]['longitude'])},
|
202 |
-
{"lat": float(coord_route[2]['latitude']), "lon": float(coord_route[2]['longitude'])},
|
203 |
-
{"lat": float(coord_route[3]['latitude']), "lon": float(coord_route[3]['longitude'])},
|
204 |
-
{"lat": float(coord_route[4]['latitude']), "lon": float(coord_route[4]['longitude'])},
|
205 |
-
{"lat": float(coord_route[5]['latitude']), "lon": float(coord_route[5]['longitude'])},
|
206 |
-
{"lat": float(coord_route[6]['latitude']), "lon": float(coord_route[6]['longitude'])},
|
207 |
-
{"lat": float(coord_route[7]['latitude']), "lon": float(coord_route[7]['longitude'])},
|
208 |
-
{"lat": float(coord_route[8]['latitude']), "lon": float(coord_route[8]['longitude'])},
|
209 |
-
{"lat": float(lat_dest), "lon": float(lon_dest)},
|
210 |
-
]
|
211 |
-
}
|
212 |
-
}
|
213 |
-
|
214 |
-
# Make the POST request
|
215 |
-
response = requests.post(url, json=payload)
|
216 |
-
|
217 |
-
# Check if the request was successful
|
218 |
-
if response.status_code == 200:
|
219 |
-
# Parse the JSON response
|
220 |
-
data = response.json()
|
221 |
-
print(json.dumps(data, indent=4))
|
222 |
-
else:
|
223 |
-
print('Failed to retrieve data:', response.status_code)
|
224 |
-
answer = ""
|
225 |
-
for result in data['results']:
|
226 |
-
name = result['poi']['name']
|
227 |
-
address = result['address']['freeformAddress']
|
228 |
-
detour_time = result['detourTime']
|
229 |
-
answer = answer + f" \nAlong the route to {city_destination}, there is the {name} at {address} that would represent a detour of {int(detour_time/60)} minutes."
|
230 |
-
|
231 |
-
return answer
|
232 |
-
|
233 |
-
|
234 |
-
#current weather API
|
235 |
-
def get_weather(city_name:str= "", **kwargs):
|
236 |
-
"""
|
237 |
-
Returns the CURRENT weather in a specified city.
|
238 |
-
Args:
|
239 |
-
city_name (string) : Required. The name of the city.
|
240 |
-
"""
|
241 |
-
# The endpoint URL provided by WeatherAPI
|
242 |
-
url = f"http://api.weatherapi.com/v1/current.json?key={WEATHER_API_KEY}&q={city_name}&aqi=no"
|
243 |
-
|
244 |
-
# Make the API request
|
245 |
-
response = requests.get(url)
|
246 |
-
|
247 |
-
if response.status_code == 200:
|
248 |
-
# Parse the JSON response
|
249 |
-
weather_data = response.json()
|
250 |
-
|
251 |
-
# Extracting the necessary pieces of data
|
252 |
-
location = weather_data['location']['name']
|
253 |
-
region = weather_data['location']['region']
|
254 |
-
country = weather_data['location']['country']
|
255 |
-
time = weather_data['location']['localtime']
|
256 |
-
temperature_c = weather_data['current']['temp_c']
|
257 |
-
condition_text = weather_data['current']['condition']['text']
|
258 |
-
wind_mph = weather_data['current']['wind_mph']
|
259 |
-
humidity = weather_data['current']['humidity']
|
260 |
-
feelslike_c = weather_data['current']['feelslike_c']
|
261 |
-
|
262 |
-
# Formulate the sentences
|
263 |
-
weather_sentences = (
|
264 |
-
f"The current weather in {location}, {region}, {country} is {condition_text} "
|
265 |
-
f"with a temperature of {temperature_c}°C that feels like {feelslike_c}°C. "
|
266 |
-
f"Humidity is at {humidity}%. "
|
267 |
-
f"Wind speed is {wind_mph} mph."
|
268 |
-
)
|
269 |
-
return weather_sentences
|
270 |
-
else:
|
271 |
-
# Handle errors
|
272 |
-
return f"Failed to get weather data: {response.status_code}, {response.text}"
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
#weather forecast API
|
277 |
-
def get_forecast(city_name:str= "", when = 0, **kwargs):
|
278 |
-
"""
|
279 |
-
Returns the weather forecast in a specified number of days for a specified city .
|
280 |
-
Args:
|
281 |
-
city_name (string) : Required. The name of the city.
|
282 |
-
when (int) : Required. in number of days (until the day for which we want to know the forecast) (example: tomorrow is 1, in two days is 2, etc.)
|
283 |
-
"""
|
284 |
-
#print(when)
|
285 |
-
when +=1
|
286 |
-
# The endpoint URL provided by WeatherAPI
|
287 |
-
url = f"http://api.weatherapi.com/v1/forecast.json?key={WEATHER_API_KEY}&q={city_name}&days={str(when)}&aqi=no"
|
288 |
-
|
289 |
-
|
290 |
-
# Make the API request
|
291 |
-
response = requests.get(url)
|
292 |
-
|
293 |
-
if response.status_code == 200:
|
294 |
-
# Parse the JSON response
|
295 |
-
data = response.json()
|
296 |
-
|
297 |
-
# Initialize an empty string to hold our result
|
298 |
-
forecast_sentences = ""
|
299 |
-
|
300 |
-
# Extract city information
|
301 |
-
location = data.get('location', {})
|
302 |
-
city_name = location.get('name', 'the specified location')
|
303 |
-
|
304 |
-
#print(data)
|
305 |
-
|
306 |
-
|
307 |
-
# Extract the forecast days
|
308 |
-
forecast_days = data.get('forecast', {}).get('forecastday', [])[when-1:]
|
309 |
-
#number = 0
|
310 |
-
|
311 |
-
#print (forecast_days)
|
312 |
-
|
313 |
-
for day in forecast_days:
|
314 |
-
date = day.get('date', 'a specific day')
|
315 |
-
conditions = day.get('day', {}).get('condition', {}).get('text', 'weather conditions')
|
316 |
-
max_temp_c = day.get('day', {}).get('maxtemp_c', 'N/A')
|
317 |
-
min_temp_c = day.get('day', {}).get('mintemp_c', 'N/A')
|
318 |
-
chance_of_rain = day.get('day', {}).get('daily_chance_of_rain', 'N/A')
|
319 |
-
|
320 |
-
if when == 1:
|
321 |
-
number_str = 'today'
|
322 |
-
elif when == 2:
|
323 |
-
number_str = 'tomorrow'
|
324 |
-
else:
|
325 |
-
number_str = f'in {when-1} days'
|
326 |
-
|
327 |
-
# Generate a sentence for the day's forecast
|
328 |
-
forecast_sentence = f"On {date} ({number_str}) in {city_name}, the weather will be {conditions} with a high of {max_temp_c}°C and a low of {min_temp_c}°C. There's a {chance_of_rain}% chance of rain. "
|
329 |
-
|
330 |
-
#number = number + 1
|
331 |
-
# Add the sentence to the result
|
332 |
-
forecast_sentences += forecast_sentence
|
333 |
-
return forecast_sentences
|
334 |
-
else:
|
335 |
-
# Handle errors
|
336 |
-
print( f"Failed to get weather data: {response.status_code}, {response.text}")
|
337 |
-
return f'error {response.status_code}'
|
338 |
-
|
339 |
|
|
|
|
|
|
|
1 |
from geopy.geocoders import Nominatim
|
2 |
|
3 |
|
|
|
51 |
lon = coord.longitude
|
52 |
return lat, lon, city
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
car_assistant_eta.ipynb
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
"cells": [
|
3 |
{
|
4 |
"cell_type": "code",
|
5 |
-
"execution_count":
|
6 |
"metadata": {},
|
7 |
"outputs": [],
|
8 |
"source": [
|
@@ -11,14 +11,14 @@
|
|
11 |
},
|
12 |
{
|
13 |
"cell_type": "code",
|
14 |
-
"execution_count":
|
15 |
"metadata": {},
|
16 |
"outputs": [],
|
17 |
"source": [
|
18 |
"from langchain_community.llms import Ollama\n",
|
19 |
"from langchain.tools.base import StructuredTool\n",
|
20 |
"\n",
|
21 |
-
"from skills import get_weather, find_route, get_forecast, vehicle_status, vehicle\n",
|
22 |
"from skills import extract_func_args\n",
|
23 |
"\n",
|
24 |
"# llm = Ollama(model=\"new-nexus\")\n",
|
@@ -27,7 +27,7 @@
|
|
27 |
},
|
28 |
{
|
29 |
"cell_type": "code",
|
30 |
-
"execution_count":
|
31 |
"metadata": {},
|
32 |
"outputs": [],
|
33 |
"source": [
|
@@ -35,12 +35,13 @@
|
|
35 |
" StructuredTool.from_function(get_weather),\n",
|
36 |
" StructuredTool.from_function(find_route),\n",
|
37 |
" StructuredTool.from_function(vehicle_status),\n",
|
|
|
38 |
"]"
|
39 |
]
|
40 |
},
|
41 |
{
|
42 |
"cell_type": "code",
|
43 |
-
"execution_count":
|
44 |
"metadata": {},
|
45 |
"outputs": [],
|
46 |
"source": [
|
@@ -56,7 +57,7 @@
|
|
56 |
},
|
57 |
{
|
58 |
"cell_type": "code",
|
59 |
-
"execution_count":
|
60 |
"metadata": {},
|
61 |
"outputs": [],
|
62 |
"source": [
|
@@ -71,7 +72,7 @@
|
|
71 |
},
|
72 |
{
|
73 |
"cell_type": "code",
|
74 |
-
"execution_count":
|
75 |
"metadata": {},
|
76 |
"outputs": [],
|
77 |
"source": [
|
@@ -91,7 +92,7 @@
|
|
91 |
},
|
92 |
{
|
93 |
"cell_type": "code",
|
94 |
-
"execution_count":
|
95 |
"metadata": {},
|
96 |
"outputs": [],
|
97 |
"source": [
|
@@ -104,16 +105,16 @@
|
|
104 |
},
|
105 |
{
|
106 |
"cell_type": "code",
|
107 |
-
"execution_count":
|
108 |
"metadata": {},
|
109 |
"outputs": [],
|
110 |
"source": [
|
111 |
-
"pp = get_prompt(RAVEN_PROMPT_FUNC, \"
|
112 |
]
|
113 |
},
|
114 |
{
|
115 |
"cell_type": "code",
|
116 |
-
"execution_count":
|
117 |
"metadata": {},
|
118 |
"outputs": [],
|
119 |
"source": [
|
@@ -122,7 +123,27 @@
|
|
122 |
},
|
123 |
{
|
124 |
"cell_type": "code",
|
125 |
-
"execution_count":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
"metadata": {},
|
127 |
"outputs": [],
|
128 |
"source": [
|
@@ -133,41 +154,43 @@
|
|
133 |
},
|
134 |
{
|
135 |
"cell_type": "code",
|
136 |
-
"execution_count":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
"metadata": {},
|
138 |
"outputs": [
|
139 |
{
|
140 |
"name": "stdout",
|
141 |
"output_type": "stream",
|
142 |
"text": [
|
143 |
-
"
|
144 |
]
|
145 |
},
|
146 |
{
|
147 |
"data": {
|
148 |
"text/plain": [
|
149 |
-
"
|
150 |
-
" {'location': {'name': 'New York',\n",
|
151 |
-
" 'region': 'New York',\n",
|
152 |
-
" 'country': 'United States of America',\n",
|
153 |
-
" 'lat': 40.71,\n",
|
154 |
-
" 'lon': -74.01,\n",
|
155 |
-
" 'tz_id': 'America/New_York',\n",
|
156 |
-
" 'localtime_epoch': 1714856966,\n",
|
157 |
-
" 'localtime': '2024-05-04 17:09'},\n",
|
158 |
-
" 'current': {'last_updated': '2024-05-04 17:00',\n",
|
159 |
-
" 'temp_c': 13.9,\n",
|
160 |
-
" 'condition': {'text': 'Partly cloudy'},\n",
|
161 |
-
" 'wind_kph': 3.6,\n",
|
162 |
-
" 'wind_dir': 'N',\n",
|
163 |
-
" 'precip_mm': 0.0,\n",
|
164 |
-
" 'precip_in': 0.0,\n",
|
165 |
-
" 'humidity': 57,\n",
|
166 |
-
" 'cloud': 75,\n",
|
167 |
-
" 'feelslike_c': 12.5}})"
|
168 |
]
|
169 |
},
|
170 |
-
"execution_count":
|
171 |
"metadata": {},
|
172 |
"output_type": "execute_result"
|
173 |
}
|
@@ -178,13 +201,130 @@
|
|
178 |
},
|
179 |
{
|
180 |
"cell_type": "code",
|
181 |
-
"execution_count":
|
182 |
"metadata": {},
|
183 |
"outputs": [],
|
184 |
"source": [
|
185 |
"#getattr(skills, func_name)(*args)"
|
186 |
]
|
187 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
{
|
189 |
"cell_type": "markdown",
|
190 |
"metadata": {},
|
|
|
2 |
"cells": [
|
3 |
{
|
4 |
"cell_type": "code",
|
5 |
+
"execution_count": 6,
|
6 |
"metadata": {},
|
7 |
"outputs": [],
|
8 |
"source": [
|
|
|
11 |
},
|
12 |
{
|
13 |
"cell_type": "code",
|
14 |
+
"execution_count": 1,
|
15 |
"metadata": {},
|
16 |
"outputs": [],
|
17 |
"source": [
|
18 |
"from langchain_community.llms import Ollama\n",
|
19 |
"from langchain.tools.base import StructuredTool\n",
|
20 |
"\n",
|
21 |
+
"from skills import get_weather, find_route, get_forecast, vehicle_status, search_points_of_interests, vehicle, search_along_route_w_coordinates\n",
|
22 |
"from skills import extract_func_args\n",
|
23 |
"\n",
|
24 |
"# llm = Ollama(model=\"new-nexus\")\n",
|
|
|
27 |
},
|
28 |
{
|
29 |
"cell_type": "code",
|
30 |
+
"execution_count": 2,
|
31 |
"metadata": {},
|
32 |
"outputs": [],
|
33 |
"source": [
|
|
|
35 |
" StructuredTool.from_function(get_weather),\n",
|
36 |
" StructuredTool.from_function(find_route),\n",
|
37 |
" StructuredTool.from_function(vehicle_status),\n",
|
38 |
+
" StructuredTool.from_function(search_points_of_interests),\n",
|
39 |
"]"
|
40 |
]
|
41 |
},
|
42 |
{
|
43 |
"cell_type": "code",
|
44 |
+
"execution_count": 3,
|
45 |
"metadata": {},
|
46 |
"outputs": [],
|
47 |
"source": [
|
|
|
57 |
},
|
58 |
{
|
59 |
"cell_type": "code",
|
60 |
+
"execution_count": 4,
|
61 |
"metadata": {},
|
62 |
"outputs": [],
|
63 |
"source": [
|
|
|
72 |
},
|
73 |
{
|
74 |
"cell_type": "code",
|
75 |
+
"execution_count": 5,
|
76 |
"metadata": {},
|
77 |
"outputs": [],
|
78 |
"source": [
|
|
|
92 |
},
|
93 |
{
|
94 |
"cell_type": "code",
|
95 |
+
"execution_count": 6,
|
96 |
"metadata": {},
|
97 |
"outputs": [],
|
98 |
"source": [
|
|
|
105 |
},
|
106 |
{
|
107 |
"cell_type": "code",
|
108 |
+
"execution_count": 7,
|
109 |
"metadata": {},
|
110 |
"outputs": [],
|
111 |
"source": [
|
112 |
+
"pp = get_prompt(RAVEN_PROMPT_FUNC, \"Are there any italian restaurants nearby?\", \"\", tools)"
|
113 |
]
|
114 |
},
|
115 |
{
|
116 |
"cell_type": "code",
|
117 |
+
"execution_count": 8,
|
118 |
"metadata": {},
|
119 |
"outputs": [],
|
120 |
"source": [
|
|
|
123 |
},
|
124 |
{
|
125 |
"cell_type": "code",
|
126 |
+
"execution_count": 9,
|
127 |
+
"metadata": {},
|
128 |
+
"outputs": [
|
129 |
+
{
|
130 |
+
"data": {
|
131 |
+
"text/plain": [
|
132 |
+
"\"Call: search_points_of_interests(search_query='italian restaurant') \""
|
133 |
+
]
|
134 |
+
},
|
135 |
+
"execution_count": 9,
|
136 |
+
"metadata": {},
|
137 |
+
"output_type": "execute_result"
|
138 |
+
}
|
139 |
+
],
|
140 |
+
"source": [
|
141 |
+
"llm_response"
|
142 |
+
]
|
143 |
+
},
|
144 |
+
{
|
145 |
+
"cell_type": "code",
|
146 |
+
"execution_count": 10,
|
147 |
"metadata": {},
|
148 |
"outputs": [],
|
149 |
"source": [
|
|
|
154 |
},
|
155 |
{
|
156 |
"cell_type": "code",
|
157 |
+
"execution_count": 11,
|
158 |
+
"metadata": {},
|
159 |
+
"outputs": [
|
160 |
+
{
|
161 |
+
"data": {
|
162 |
+
"text/plain": [
|
163 |
+
"('search_points_of_interests', {'search_query': 'italian restaurant'})"
|
164 |
+
]
|
165 |
+
},
|
166 |
+
"execution_count": 11,
|
167 |
+
"metadata": {},
|
168 |
+
"output_type": "execute_result"
|
169 |
+
}
|
170 |
+
],
|
171 |
+
"source": [
|
172 |
+
"func_name, kwargs"
|
173 |
+
]
|
174 |
+
},
|
175 |
+
{
|
176 |
+
"cell_type": "code",
|
177 |
+
"execution_count": 12,
|
178 |
"metadata": {},
|
179 |
"outputs": [
|
180 |
{
|
181 |
"name": "stdout",
|
182 |
"output_type": "stream",
|
183 |
"text": [
|
184 |
+
"POI search vehicle's lat: 49.6002, lon: 6.1296\n"
|
185 |
]
|
186 |
},
|
187 |
{
|
188 |
"data": {
|
189 |
"text/plain": [
|
190 |
+
"'There are 30 options in the vicinity. The most relevant are: Luci-Italian Restaurant is 823 meters away.\\n La Briscola is 171 meters away.\\n Casa Giuditta is 92 meters away'"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
]
|
192 |
},
|
193 |
+
"execution_count": 12,
|
194 |
"metadata": {},
|
195 |
"output_type": "execute_result"
|
196 |
}
|
|
|
201 |
},
|
202 |
{
|
203 |
"cell_type": "code",
|
204 |
+
"execution_count": 13,
|
205 |
"metadata": {},
|
206 |
"outputs": [],
|
207 |
"source": [
|
208 |
"#getattr(skills, func_name)(*args)"
|
209 |
]
|
210 |
},
|
211 |
+
{
|
212 |
+
"cell_type": "markdown",
|
213 |
+
"metadata": {},
|
214 |
+
"source": [
|
215 |
+
"## Categories POI"
|
216 |
+
]
|
217 |
+
},
|
218 |
+
{
|
219 |
+
"cell_type": "code",
|
220 |
+
"execution_count": 14,
|
221 |
+
"metadata": {},
|
222 |
+
"outputs": [],
|
223 |
+
"source": [
|
224 |
+
"import requests\n",
|
225 |
+
"from skills.common import config"
|
226 |
+
]
|
227 |
+
},
|
228 |
+
{
|
229 |
+
"cell_type": "code",
|
230 |
+
"execution_count": 15,
|
231 |
+
"metadata": {},
|
232 |
+
"outputs": [],
|
233 |
+
"source": [
|
234 |
+
"res = requests.get(f\"https://api.tomtom.com/search/2/poiCategories.json?key={config.TOMTOM_API_KEY}\")"
|
235 |
+
]
|
236 |
+
},
|
237 |
+
{
|
238 |
+
"cell_type": "code",
|
239 |
+
"execution_count": 16,
|
240 |
+
"metadata": {},
|
241 |
+
"outputs": [],
|
242 |
+
"source": [
|
243 |
+
"data = res.json()"
|
244 |
+
]
|
245 |
+
},
|
246 |
+
{
|
247 |
+
"cell_type": "code",
|
248 |
+
"execution_count": 17,
|
249 |
+
"metadata": {},
|
250 |
+
"outputs": [],
|
251 |
+
"source": [
|
252 |
+
"data = data[\"poiCategories\"]"
|
253 |
+
]
|
254 |
+
},
|
255 |
+
{
|
256 |
+
"cell_type": "code",
|
257 |
+
"execution_count": 18,
|
258 |
+
"metadata": {},
|
259 |
+
"outputs": [],
|
260 |
+
"source": [
|
261 |
+
"categories = [dict(id=category[\"id\"],name=category[\"name\"]) for category in data]"
|
262 |
+
]
|
263 |
+
},
|
264 |
+
{
|
265 |
+
"cell_type": "code",
|
266 |
+
"execution_count": 19,
|
267 |
+
"metadata": {},
|
268 |
+
"outputs": [],
|
269 |
+
"source": [
|
270 |
+
"def search_categories(categories, search_term):\n",
|
271 |
+
" return [category for category in categories if search_term.lower() in category[\"name\"].lower()]"
|
272 |
+
]
|
273 |
+
},
|
274 |
+
{
|
275 |
+
"cell_type": "code",
|
276 |
+
"execution_count": 20,
|
277 |
+
"metadata": {},
|
278 |
+
"outputs": [
|
279 |
+
{
|
280 |
+
"data": {
|
281 |
+
"text/plain": [
|
282 |
+
"[{'id': 7315044, 'name': 'Spanish Restaurant'}]"
|
283 |
+
]
|
284 |
+
},
|
285 |
+
"execution_count": 20,
|
286 |
+
"metadata": {},
|
287 |
+
"output_type": "execute_result"
|
288 |
+
}
|
289 |
+
],
|
290 |
+
"source": [
|
291 |
+
"search_categories(categories, \"spa\")"
|
292 |
+
]
|
293 |
+
},
|
294 |
+
{
|
295 |
+
"cell_type": "markdown",
|
296 |
+
"metadata": {},
|
297 |
+
"source": [
|
298 |
+
"## Test POI Along Route"
|
299 |
+
]
|
300 |
+
},
|
301 |
+
{
|
302 |
+
"cell_type": "code",
|
303 |
+
"execution_count": 21,
|
304 |
+
"metadata": {},
|
305 |
+
"outputs": [
|
306 |
+
{
|
307 |
+
"name": "stdout",
|
308 |
+
"output_type": "stream",
|
309 |
+
"text": [
|
310 |
+
"lat_dest: 49.5206, lon_dest: 6.29716\n",
|
311 |
+
"lat_depart: 49.6002, lon_depart: 6.1296\n"
|
312 |
+
]
|
313 |
+
}
|
314 |
+
],
|
315 |
+
"source": [
|
316 |
+
"output, points = find_route(\"Ellange, Luxembourg\")"
|
317 |
+
]
|
318 |
+
},
|
319 |
+
{
|
320 |
+
"cell_type": "code",
|
321 |
+
"execution_count": null,
|
322 |
+
"metadata": {},
|
323 |
+
"outputs": [],
|
324 |
+
"source": [
|
325 |
+
"search"
|
326 |
+
]
|
327 |
+
},
|
328 |
{
|
329 |
"cell_type": "markdown",
|
330 |
"metadata": {},
|
skills/__init__.py
CHANGED
@@ -3,6 +3,7 @@ import inspect
|
|
3 |
from .common import execute_function_call, extract_func_args, vehicle
|
4 |
from .weather import get_weather, get_forecast
|
5 |
from .routing import find_route
|
|
|
6 |
from .vehicle import vehicle_status
|
7 |
|
8 |
|
@@ -22,4 +23,4 @@ def format_functions_for_prompt_raven(*functions):
|
|
22 |
return "\n".join(formatted_functions)
|
23 |
|
24 |
|
25 |
-
SKILLS_PROMPT = format_functions_for_prompt_raven(get_weather, get_forecast, find_route)
|
|
|
3 |
from .common import execute_function_call, extract_func_args, vehicle
|
4 |
from .weather import get_weather, get_forecast
|
5 |
from .routing import find_route
|
6 |
+
from .poi import search_points_of_interests, search_along_route_w_coordinates
|
7 |
from .vehicle import vehicle_status
|
8 |
|
9 |
|
|
|
23 |
return "\n".join(formatted_functions)
|
24 |
|
25 |
|
26 |
+
SKILLS_PROMPT = format_functions_for_prompt_raven(get_weather, get_forecast, find_route, search_points_of_interests)
|
skills/common.py
CHANGED
@@ -59,4 +59,4 @@ vehicle = VehicleStatus(
|
|
59 |
date="2025-03-29",
|
60 |
time="08:00:20",
|
61 |
destination="Kirchberg Campus, Kirchberg"
|
62 |
-
)
|
|
|
59 |
date="2025-03-29",
|
60 |
time="08:00:20",
|
61 |
destination="Kirchberg Campus, Kirchberg"
|
62 |
+
)
|
skills/poi.py
CHANGED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import requests
|
3 |
+
from .common import config, vehicle
|
4 |
+
|
5 |
+
|
6 |
+
# Select coordinates at equal distance, including the last one
|
7 |
+
def select_equally_spaced_coordinates(coords, number_of_points=10):
|
8 |
+
n = len(coords)
|
9 |
+
selected_coords = []
|
10 |
+
interval = max((n - 1) / (number_of_points - 1), 1)
|
11 |
+
for i in range(number_of_points):
|
12 |
+
# Calculate the index, ensuring it doesn't exceed the bounds of the list
|
13 |
+
index = int(round(i * interval))
|
14 |
+
if index < n:
|
15 |
+
selected_coords.append(coords[index])
|
16 |
+
return selected_coords
|
17 |
+
|
18 |
+
|
19 |
+
def search_points_of_interests(search_query="french restaurant"):
|
20 |
+
"""
|
21 |
+
Return some of the closest points of interest matching the query.
|
22 |
+
:param search_query (string): Required. Describing the type of point of interest depending on what the user wants to do. Make sure to include the type of POI you are looking for. For example italian restaurant, grocery shop, etc.
|
23 |
+
"""
|
24 |
+
|
25 |
+
# Extract the latitude and longitude of the vehicle
|
26 |
+
vehicle_coordinates = getattr(vehicle, "location_coordinates")
|
27 |
+
lat, lon = vehicle_coordinates
|
28 |
+
print(f"POI search vehicle's lat: {lat}, lon: {lon}")
|
29 |
+
|
30 |
+
# https://developer.tomtom.com/search-api/documentation/search-service/search
|
31 |
+
r = requests.get(
|
32 |
+
f"https://api.tomtom.com/search/2/search/{search_query}.json?key={config.TOMTOM_API_KEY}&lat={lat}&lon={lon}&category&radius=1000&limit=100",
|
33 |
+
timeout=5,
|
34 |
+
)
|
35 |
+
|
36 |
+
# Parse JSON from the response
|
37 |
+
data = r.json()
|
38 |
+
# Extract results
|
39 |
+
results = data["results"]
|
40 |
+
|
41 |
+
# TODO: Handle the no results case.
|
42 |
+
if not results:
|
43 |
+
return "No results found in the vicinity."
|
44 |
+
|
45 |
+
# Sort the results based on distance
|
46 |
+
# sorted_results = sorted(results, key=lambda x: x["dist"])
|
47 |
+
# print(sorted_results)
|
48 |
+
|
49 |
+
# Format and limit to top 5 results
|
50 |
+
formatted_results = [
|
51 |
+
f"{result['poi']['name']} is {int(result['dist'])} meters away"
|
52 |
+
for result in results[:3]
|
53 |
+
]
|
54 |
+
|
55 |
+
output = (
|
56 |
+
f"There are {len(results)} options in the vicinity. The most relevant are: "
|
57 |
+
)
|
58 |
+
return output + ".\n ".join(formatted_results)
|
59 |
+
|
60 |
+
|
61 |
+
def find_points_of_interest(lat="0", lon="0", type_of_poi="restaurant"):
|
62 |
+
"""
|
63 |
+
Return some of the closest points of interest for a specific location and type of point of interest. The more parameters there are, the more precise.
|
64 |
+
:param lat (string): latitude
|
65 |
+
:param lon (string): longitude
|
66 |
+
:param city (string): Required. city
|
67 |
+
:param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
|
68 |
+
"""
|
69 |
+
# https://developer.tomtom.com/search-api/documentation/search-service/points-of-interest-search
|
70 |
+
r = requests.get(
|
71 |
+
f"https://api.tomtom.com/search/2/search/{type_of_poi}"
|
72 |
+
".json?key={0}&lat={1}&lon={2}&radius=10000&vehicleTypeSet=Car&idxSet=POI&limit=100".format(
|
73 |
+
config.TOMTOM_API_KEY, lat, lon
|
74 |
+
)
|
75 |
+
)
|
76 |
+
|
77 |
+
# Parse JSON from the response
|
78 |
+
data = r.json()
|
79 |
+
# print(data)
|
80 |
+
# Extract results
|
81 |
+
results = data["results"]
|
82 |
+
|
83 |
+
# Sort the results based on distance
|
84 |
+
sorted_results = sorted(results, key=lambda x: x["dist"])
|
85 |
+
# print(sorted_results)
|
86 |
+
|
87 |
+
# Format and limit to top 5 results
|
88 |
+
formatted_results = [
|
89 |
+
f"The {type_of_poi} {result['poi']['name']} is {int(result['dist'])} meters away"
|
90 |
+
for result in sorted_results[:5]
|
91 |
+
]
|
92 |
+
|
93 |
+
return ". ".join(formatted_results)
|
94 |
+
|
95 |
+
|
96 |
+
def search_along_route_w_coordinates(points: list[tuple[float, float]], query: str):
|
97 |
+
"""
|
98 |
+
Return some of the closest points of interest along the route from the depart point, specified by its coordinates.
|
99 |
+
:param points (list[tuple(float, float)]): Required. List of tuples of latitude and longitude of the points along the route.
|
100 |
+
:param query (string): Required. type of point of interest depending on what the user wants to do.
|
101 |
+
"""
|
102 |
+
|
103 |
+
# The API endpoint for searching along a route
|
104 |
+
url = f"https://api.tomtom.com/search/2/searchAlongRoute/{query}.json?key={config.TOMTOM_API_KEY}&maxDetourTime=360&limit=20&sortBy=detourTime"
|
105 |
+
|
106 |
+
points = select_equally_spaced_coordinates(points, number_of_points=20)
|
107 |
+
|
108 |
+
# The data payload
|
109 |
+
payload = {
|
110 |
+
"route": {
|
111 |
+
"points": [{"lat": pt["latitude"], "lon": pt["longitude"]} for pt in points]
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
# Make the POST request
|
116 |
+
response = requests.post(url, json=payload, timeout=5)
|
117 |
+
|
118 |
+
# Check if the request was successful
|
119 |
+
if response.status_code == 200:
|
120 |
+
# Parse the JSON response
|
121 |
+
data = response.json()
|
122 |
+
# print(json.dumps(data, indent=4))
|
123 |
+
else:
|
124 |
+
print("Failed to retrieve data:", response.status_code)
|
125 |
+
return "Failed to retrieve data. Please try again."
|
126 |
+
answer = ""
|
127 |
+
if not data["results"]:
|
128 |
+
return "No results found along the way."
|
129 |
+
|
130 |
+
if len(data["results"]) == 20:
|
131 |
+
answer = "There more than 20 results along the way. Here are the top 3 results:"
|
132 |
+
elif len(data["results"]) > 3:
|
133 |
+
answer = f"There are {len(data['results'])} results along the way. Here are the top 3 results:"
|
134 |
+
for result in data["results"][:3]:
|
135 |
+
name = result["poi"]["name"]
|
136 |
+
address = result["address"]["freeformAddress"]
|
137 |
+
detour_time = result["detourTime"]
|
138 |
+
answer = (
|
139 |
+
answer
|
140 |
+
+ f" \n{name} at {address} would require a detour of {int(detour_time/60)} minutes."
|
141 |
+
)
|
142 |
+
|
143 |
+
return answer
|
skills/routing.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1 |
from datetime import datetime
|
2 |
-
|
3 |
import requests
|
4 |
-
|
5 |
from .common import config, vehicle
|
6 |
|
7 |
|
@@ -10,6 +8,7 @@ def find_coordinates(address):
|
|
10 |
Find the coordinates of a specific address.
|
11 |
:param address (string): Required. The address
|
12 |
"""
|
|
|
13 |
url = f"https://api.tomtom.com/search/2/geocode/{address}.json?key={config.TOMTOM_API_KEY}"
|
14 |
response = requests.get(url)
|
15 |
data = response.json()
|
@@ -19,11 +18,10 @@ def find_coordinates(address):
|
|
19 |
|
20 |
|
21 |
def calculate_route():
|
22 |
-
api_key = "api_key"
|
23 |
origin = "49.631997,6.171029"
|
24 |
destination = "49.586745,6.140002"
|
25 |
|
26 |
-
url = f"https://api.tomtom.com/routing/1/calculateRoute/{origin}:{destination}/json?key={
|
27 |
response = requests.get(url)
|
28 |
data = response.json()
|
29 |
|
@@ -65,7 +63,8 @@ def find_route_tomtom(
|
|
65 |
:param lon_dest (string): longitude of destination
|
66 |
:param depart_time (string): departure hour, in the format '08:00:20'.
|
67 |
"""
|
68 |
-
|
|
|
69 |
r = requests.get(
|
70 |
f"https://api.tomtom.com/routing/1/calculateRoute/{lat_depart},{lon_depart}:{lat_dest},{lon_dest}/json?key={config.TOMTOM_API_KEY}&departAt={depart_datetime}",
|
71 |
timeout=5,
|
@@ -132,4 +131,4 @@ def find_route(destination=""):
|
|
132 |
arrival_hour_display = arrival_time.strftime("%H:%M")
|
133 |
|
134 |
# return the distance and time
|
135 |
-
return f"
|
|
|
1 |
from datetime import datetime
|
|
|
2 |
import requests
|
|
|
3 |
from .common import config, vehicle
|
4 |
|
5 |
|
|
|
8 |
Find the coordinates of a specific address.
|
9 |
:param address (string): Required. The address
|
10 |
"""
|
11 |
+
# https://developer.tomtom.com/geocoding-api/documentation/geocode
|
12 |
url = f"https://api.tomtom.com/search/2/geocode/{address}.json?key={config.TOMTOM_API_KEY}"
|
13 |
response = requests.get(url)
|
14 |
data = response.json()
|
|
|
18 |
|
19 |
|
20 |
def calculate_route():
|
|
|
21 |
origin = "49.631997,6.171029"
|
22 |
destination = "49.586745,6.140002"
|
23 |
|
24 |
+
url = f"https://api.tomtom.com/routing/1/calculateRoute/{origin}:{destination}/json?key={config.TOMTOM_API_KEY}"
|
25 |
response = requests.get(url)
|
26 |
data = response.json()
|
27 |
|
|
|
63 |
:param lon_dest (string): longitude of destination
|
64 |
:param depart_time (string): departure hour, in the format '08:00:20'.
|
65 |
"""
|
66 |
+
# https://developer.tomtom.com/routing-api/documentation/routing/calculate-route
|
67 |
+
# https://developer.tomtom.com/routing-api/documentation/routing/guidance-instructions
|
68 |
r = requests.get(
|
69 |
f"https://api.tomtom.com/routing/1/calculateRoute/{lat_depart},{lon_depart}:{lat_dest},{lon_dest}/json?key={config.TOMTOM_API_KEY}&departAt={depart_datetime}",
|
70 |
timeout=5,
|
|
|
131 |
arrival_hour_display = arrival_time.strftime("%H:%M")
|
132 |
|
133 |
# return the distance and time
|
134 |
+
return f"The route to {destination} is {distance_km:.2f} km and {time_display}. Leaving now, the arrival time is estimated at {arrival_hour_display}.", raw_response["routes"][0]["legs"][0]["points"]
|
skills/vehicle.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
from
|
2 |
|
3 |
|
4 |
STATUS_TEMPLATE = """We are at {location}, current time: {time}, current date: {date} and our destination is: {destination}.
|
|
|
1 |
+
from .common import vehicle
|
2 |
|
3 |
|
4 |
STATUS_TEMPLATE = """We are at {location}, current time: {time}, current date: {date} and our destination is: {destination}.
|