chore: Update vehicle destination in calculate_route function
Browse files- kitt.py +144 -23
- skills/__init__.py +18 -1
- skills/routing.py +4 -6
- skills/vehicle.py +13 -9
- skills/weather.py +8 -3
kitt.py
CHANGED
@@ -4,19 +4,132 @@ import requests
|
|
4 |
import skills
|
5 |
from skills.common import config, vehicle
|
6 |
from skills.routing import calculate_route
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
|
9 |
# Generate options for hours (00-23)
|
10 |
hour_options = [f"{i:02d}:00" for i in range(24)]
|
11 |
|
|
|
12 |
def set_time(time_picker):
|
13 |
vehicle.time = time_picker
|
14 |
return vehicle.model_dump_json()
|
15 |
|
|
|
16 |
def get_vehicle_status(state):
|
17 |
return state.value["vehicle"].model_dump_json()
|
18 |
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
# to be able to use the microphone on chrome, you will have to go to chrome://flags/#unsafely-treat-insecure-origin-as-secure and enter http://10.186.115.21:7860/
|
21 |
# in "Insecure origins treated as secure", enable it and relaunch chrome
|
22 |
|
@@ -25,27 +138,28 @@ def get_vehicle_status(state):
|
|
25 |
# What's the closest restaurant from here?
|
26 |
|
27 |
|
28 |
-
model_answer = ""
|
29 |
-
general_context = ""
|
30 |
-
# Define the initial state with some initial context.
|
31 |
-
print(general_context)
|
32 |
-
initial_state = {"context": general_context}
|
33 |
-
initial_context = initial_state["context"]
|
34 |
-
# Create the Gradio interface.
|
35 |
-
|
36 |
-
|
37 |
with gr.Blocks(theme=gr.themes.Default()) as demo:
|
38 |
state = gr.State(
|
39 |
-
value={
|
|
|
|
|
|
|
|
|
40 |
)
|
41 |
|
42 |
with gr.Row():
|
43 |
with gr.Column(scale=1, min_width=300):
|
44 |
time_picker = gr.Dropdown(
|
45 |
-
choices=hour_options,
|
|
|
|
|
|
|
46 |
)
|
47 |
history = gr.Radio(
|
48 |
-
["Yes", "No"],
|
|
|
|
|
|
|
49 |
)
|
50 |
voice_character = gr.Radio(
|
51 |
choices=[
|
@@ -76,7 +190,7 @@ with gr.Blocks(theme=gr.themes.Default()) as demo:
|
|
76 |
|
77 |
with gr.Column(scale=2, min_width=600):
|
78 |
map_plot = gr.Plot()
|
79 |
-
|
80 |
# map_if = gr.Interface(fn=plot_map, inputs=year_input, outputs=map_plot)
|
81 |
|
82 |
with gr.Row():
|
@@ -87,14 +201,12 @@ with gr.Blocks(theme=gr.themes.Default()) as demo:
|
|
87 |
input_text = gr.Textbox(
|
88 |
value="How is the weather?", label="Input text", interactive=True
|
89 |
)
|
90 |
-
vehicle_status = gr.
|
91 |
-
value=
|
92 |
)
|
93 |
with gr.Column():
|
94 |
output_audio = gr.Audio(label="output audio")
|
95 |
-
output_text = gr.TextArea(
|
96 |
-
value="", label="Output text", interactive=False
|
97 |
-
)
|
98 |
# iface = gr.Interface(
|
99 |
# fn=transcript,
|
100 |
# inputs=[
|
@@ -113,19 +225,28 @@ with gr.Blocks(theme=gr.themes.Default()) as demo:
|
|
113 |
|
114 |
# Update plot based on the origin and destination
|
115 |
# Sets the current location and destination
|
116 |
-
origin.submit(
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
# Update time based on the time picker
|
120 |
time_picker.select(fn=set_time, inputs=[time_picker], outputs=[vehicle_status])
|
121 |
|
|
|
|
|
|
|
122 |
# close all interfaces open to make the port available
|
123 |
gr.close_all()
|
124 |
# Launch the interface.
|
125 |
|
126 |
if __name__ == "__main__":
|
127 |
-
demo.launch(
|
128 |
-
debug=True, server_name="0.0.0.0", server_port=7860, ssl_verify=False
|
129 |
-
)
|
130 |
|
131 |
# iface.launch(debug=True, share=False, server_name="0.0.0.0", server_port=7860, ssl_verify=False)
|
|
|
4 |
import skills
|
5 |
from skills.common import config, vehicle
|
6 |
from skills.routing import calculate_route
|
7 |
+
import ollama
|
8 |
+
|
9 |
+
### LLM Stuff ###
|
10 |
+
from langchain_community.llms import Ollama
|
11 |
+
from langchain.tools.base import StructuredTool
|
12 |
+
|
13 |
+
from skills import (
|
14 |
+
get_weather,
|
15 |
+
find_route,
|
16 |
+
get_forecast,
|
17 |
+
vehicle_status as vehicle_status_fn,
|
18 |
+
search_points_of_interests,
|
19 |
+
search_along_route_w_coordinates,
|
20 |
+
do_anything_else,
|
21 |
+
date_time_info
|
22 |
+
)
|
23 |
+
from skills import extract_func_args
|
24 |
+
|
25 |
+
|
26 |
+
global_context = {
|
27 |
+
"vehicle": vehicle,
|
28 |
+
"query": "How is the weather?",
|
29 |
+
"route_points": [],
|
30 |
+
}
|
31 |
+
|
32 |
+
|
33 |
+
MODEL_FUNC = "nexusraven"
|
34 |
+
MODEL_GENERAL = "llama3:instruct"
|
35 |
+
|
36 |
+
RAVEN_PROMPT_FUNC = """You are a helpful AI assistant in a car (vehicle), that follows instructions extremely well. \
|
37 |
+
Answer questions concisely and do not mention what you base your reply on."
|
38 |
+
|
39 |
+
{raven_tools}
|
40 |
+
|
41 |
+
{history}
|
42 |
+
|
43 |
+
User Query: Question: {input}<human_end>
|
44 |
+
"""
|
45 |
+
|
46 |
+
def get_prompt(template, input, history, tools):
|
47 |
+
# "vehicle_status": vehicle_status_fn()[0]
|
48 |
+
kwargs = {"history": history, "input": input}
|
49 |
+
prompt = "<human>:\n"
|
50 |
+
for tool in tools:
|
51 |
+
func_signature, func_docstring = tool.description.split(" - ", 1)
|
52 |
+
prompt += f'Function:\n<func_start>def {func_signature}<func_end>\n<docstring_start>\n"""\n{func_docstring}\n"""\n<docstring_end>\n'
|
53 |
+
kwargs["raven_tools"] = prompt
|
54 |
+
|
55 |
+
if history:
|
56 |
+
kwargs["history"] = f"Previous conversation history:{history}\n"
|
57 |
+
|
58 |
+
return template.format(**kwargs).replace("{{", "{").replace("}}", "}")
|
59 |
+
|
60 |
+
def use_tool(func_name, kwargs, tools):
|
61 |
+
for tool in tools:
|
62 |
+
if tool.name == func_name:
|
63 |
+
return tool.invoke(input=kwargs)
|
64 |
+
return None
|
65 |
+
|
66 |
+
tools = [
|
67 |
+
StructuredTool.from_function(get_weather),
|
68 |
+
StructuredTool.from_function(find_route),
|
69 |
+
# StructuredTool.from_function(vehicle_status),
|
70 |
+
StructuredTool.from_function(search_points_of_interests),
|
71 |
+
StructuredTool.from_function(search_along_route_w_coordinates),
|
72 |
+
StructuredTool.from_function(date_time_info),
|
73 |
+
StructuredTool.from_function(do_anything_else),
|
74 |
+
]
|
75 |
+
# llm = Ollama(model="nexusraven", stop=["\nReflection:", "\nThought:"], keep_alive=60*10)
|
76 |
|
77 |
|
78 |
# Generate options for hours (00-23)
|
79 |
hour_options = [f"{i:02d}:00" for i in range(24)]
|
80 |
|
81 |
+
|
82 |
def set_time(time_picker):
|
83 |
vehicle.time = time_picker
|
84 |
return vehicle.model_dump_json()
|
85 |
|
86 |
+
|
87 |
def get_vehicle_status(state):
|
88 |
return state.value["vehicle"].model_dump_json()
|
89 |
|
90 |
|
91 |
+
def run_generic_model(query):
|
92 |
+
print(f"Running the generic model with query: {query}")
|
93 |
+
data = {
|
94 |
+
"prompt": query,
|
95 |
+
"model": MODEL_GENERAL,
|
96 |
+
"options": {
|
97 |
+
# "temperature": 0.1,
|
98 |
+
# "stop":["\nReflection:", "\nThought:"]
|
99 |
+
}
|
100 |
+
}
|
101 |
+
out = ollama.generate(**data)
|
102 |
+
return out["response"]
|
103 |
+
|
104 |
+
|
105 |
+
def run_model(query):
|
106 |
+
print("Query: ", query)
|
107 |
+
global_context["query"] = query
|
108 |
+
global_context["prompt"] = get_prompt(RAVEN_PROMPT_FUNC, query, "", tools)
|
109 |
+
print("Prompt: ", global_context["prompt"])
|
110 |
+
data = {
|
111 |
+
"prompt": global_context["prompt"],
|
112 |
+
# "streaming": False,
|
113 |
+
"model": "nexusraven",
|
114 |
+
# "model": "smangrul/llama-3-8b-instruct-function-calling",
|
115 |
+
"raw": True,
|
116 |
+
"options": {
|
117 |
+
"temperature": 0.5,
|
118 |
+
"stop":["\nReflection:", "\nThought:"]
|
119 |
+
}
|
120 |
+
}
|
121 |
+
out = ollama.generate(**data)
|
122 |
+
llm_response = out["response"]
|
123 |
+
if "Call: " in llm_response:
|
124 |
+
func_name, kwargs = extract_func_args(llm_response)
|
125 |
+
print(f"Function: {func_name}, Args: {kwargs}")
|
126 |
+
if func_name == "do_anything_else":
|
127 |
+
return run_generic_model(query)
|
128 |
+
|
129 |
+
return use_tool(func_name, kwargs, tools)
|
130 |
+
return out["response"]
|
131 |
+
|
132 |
+
|
133 |
# to be able to use the microphone on chrome, you will have to go to chrome://flags/#unsafely-treat-insecure-origin-as-secure and enter http://10.186.115.21:7860/
|
134 |
# in "Insecure origins treated as secure", enable it and relaunch chrome
|
135 |
|
|
|
138 |
# What's the closest restaurant from here?
|
139 |
|
140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
with gr.Blocks(theme=gr.themes.Default()) as demo:
|
142 |
state = gr.State(
|
143 |
+
value={
|
144 |
+
# "context": initial_context,
|
145 |
+
"query": "",
|
146 |
+
"route_points": [],
|
147 |
+
}
|
148 |
)
|
149 |
|
150 |
with gr.Row():
|
151 |
with gr.Column(scale=1, min_width=300):
|
152 |
time_picker = gr.Dropdown(
|
153 |
+
choices=hour_options,
|
154 |
+
label="What time is it? (HH:MM)",
|
155 |
+
value="08:00",
|
156 |
+
interactive=True,
|
157 |
)
|
158 |
history = gr.Radio(
|
159 |
+
["Yes", "No"],
|
160 |
+
label="Maintain the conversation history?",
|
161 |
+
value="No",
|
162 |
+
interactive=True,
|
163 |
)
|
164 |
voice_character = gr.Radio(
|
165 |
choices=[
|
|
|
190 |
|
191 |
with gr.Column(scale=2, min_width=600):
|
192 |
map_plot = gr.Plot()
|
193 |
+
|
194 |
# map_if = gr.Interface(fn=plot_map, inputs=year_input, outputs=map_plot)
|
195 |
|
196 |
with gr.Row():
|
|
|
201 |
input_text = gr.Textbox(
|
202 |
value="How is the weather?", label="Input text", interactive=True
|
203 |
)
|
204 |
+
vehicle_status = gr.JSON(
|
205 |
+
value=vehicle.model_dump_json(), label="Vehicle status"
|
206 |
)
|
207 |
with gr.Column():
|
208 |
output_audio = gr.Audio(label="output audio")
|
209 |
+
output_text = gr.TextArea(value="", label="Output text", interactive=False)
|
|
|
|
|
210 |
# iface = gr.Interface(
|
211 |
# fn=transcript,
|
212 |
# inputs=[
|
|
|
225 |
|
226 |
# Update plot based on the origin and destination
|
227 |
# Sets the current location and destination
|
228 |
+
origin.submit(
|
229 |
+
fn=calculate_route,
|
230 |
+
inputs=[origin, destination],
|
231 |
+
outputs=[map_plot, vehicle_status],
|
232 |
+
)
|
233 |
+
destination.submit(
|
234 |
+
fn=calculate_route,
|
235 |
+
inputs=[origin, destination],
|
236 |
+
outputs=[map_plot, vehicle_status],
|
237 |
+
)
|
238 |
|
239 |
# Update time based on the time picker
|
240 |
time_picker.select(fn=set_time, inputs=[time_picker], outputs=[vehicle_status])
|
241 |
|
242 |
+
# Run the model if the input text is changed
|
243 |
+
input_text.submit(fn=run_model, inputs=[input_text], outputs=[output_text])
|
244 |
+
|
245 |
# close all interfaces open to make the port available
|
246 |
gr.close_all()
|
247 |
# Launch the interface.
|
248 |
|
249 |
if __name__ == "__main__":
|
250 |
+
demo.launch(debug=True, server_name="0.0.0.0", server_port=7860, ssl_verify=False)
|
|
|
|
|
251 |
|
252 |
# iface.launch(debug=True, share=False, server_name="0.0.0.0", server_port=7860, ssl_verify=False)
|
skills/__init__.py
CHANGED
@@ -1,12 +1,29 @@
|
|
|
|
1 |
import inspect
|
2 |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
def format_functions_for_prompt_raven(*functions):
|
11 |
"""Format functions for use in Prompt Raven.
|
12 |
|
|
|
1 |
+
from datetime import datetime
|
2 |
import inspect
|
3 |
|
4 |
+
from .common import execute_function_call, extract_func_args, vehicle as vehicle_obj
|
5 |
from .weather import get_weather, get_forecast
|
6 |
from .routing import find_route
|
7 |
from .poi import search_points_of_interests, search_along_route_w_coordinates
|
8 |
from .vehicle import vehicle_status
|
9 |
|
10 |
|
11 |
+
|
12 |
+
def date_time_info():
|
13 |
+
"""Get the current date and time."""
|
14 |
+
time = getattr(vehicle_obj, "time")
|
15 |
+
date = getattr(vehicle_obj, "date")
|
16 |
+
datetime_obj = datetime.fromisoformat(f"{date}T{time}")
|
17 |
+
human_readable_datetime = datetime_obj.strftime("%I:%M %p %A, %B %d, %Y")
|
18 |
+
return f"It is {human_readable_datetime}."
|
19 |
+
|
20 |
+
|
21 |
+
def do_anything_else():
|
22 |
+
"""If the user wants to do anything else call this function. If the question doesn't match any of the functions use this one."""
|
23 |
+
return True
|
24 |
+
|
25 |
+
|
26 |
+
|
27 |
def format_functions_for_prompt_raven(*functions):
|
28 |
"""Format functions for use in Prompt Raven.
|
29 |
|
skills/routing.py
CHANGED
@@ -109,8 +109,8 @@ def find_route_tomtom(
|
|
109 |
|
110 |
|
111 |
def find_route(destination=""):
|
112 |
-
"""
|
113 |
-
|
114 |
:param destination (string): Required. The destination
|
115 |
"""
|
116 |
# lat, lon, city = check_city_coordinates(lat_depart,lon_depart,city_depart)
|
@@ -153,7 +153,5 @@ def find_route(destination=""):
|
|
153 |
arrival_hour_display = arrival_time.strftime("%H:%M")
|
154 |
|
155 |
# return the distance and time
|
156 |
-
return
|
157 |
-
|
158 |
-
raw_response["routes"][0]["legs"][0]["points"],
|
159 |
-
)
|
|
|
109 |
|
110 |
|
111 |
def find_route(destination=""):
|
112 |
+
"""This function finds a route to a destination and returns the distance and the estimated time to go to a specific destination\
|
113 |
+
from the current location.
|
114 |
:param destination (string): Required. The destination
|
115 |
"""
|
116 |
# lat, lon, city = check_city_coordinates(lat_depart,lon_depart,city_depart)
|
|
|
153 |
arrival_hour_display = arrival_time.strftime("%H:%M")
|
154 |
|
155 |
# return the distance and time
|
156 |
+
return f"The route to {destination} is {distance_km:.2f} km which takes {time_display}. Leaving now, the arrival time is estimated at {arrival_hour_display}."
|
157 |
+
# raw_response["routes"][0]["legs"][0]["points"]
|
|
|
|
skills/vehicle.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1 |
from .common import vehicle
|
2 |
|
3 |
|
4 |
-
STATUS_TEMPLATE = """
|
|
|
|
|
5 |
"""
|
6 |
|
7 |
|
@@ -19,13 +21,15 @@ def vehicle_status() -> tuple[str, dict[str, str]]:
|
|
19 |
"destination": "Kirchberg Campus, Kirchberg"
|
20 |
}
|
21 |
"""
|
22 |
-
vs = {
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
}
|
30 |
vs = vehicle.dict()
|
|
|
|
|
31 |
return STATUS_TEMPLATE.format(**vs), vs
|
|
|
1 |
from .common import vehicle
|
2 |
|
3 |
|
4 |
+
STATUS_TEMPLATE = """
|
5 |
+
We are at {location}, coordinates: {lat}, {lon},
|
6 |
+
current time: {time}, current date: {date} and our destination is: {destination}.
|
7 |
"""
|
8 |
|
9 |
|
|
|
21 |
"destination": "Kirchberg Campus, Kirchberg"
|
22 |
}
|
23 |
"""
|
24 |
+
# vs = {
|
25 |
+
# "location": "Luxembourg Gare, Luxembourg",
|
26 |
+
# "lat": 49.6000,
|
27 |
+
# "lon": 6.1333,
|
28 |
+
# "date": "2025-03-29",
|
29 |
+
# "time": "08:00:20",
|
30 |
+
# "destination": "Kirchberg Campus, Luxembourg"
|
31 |
+
# }
|
32 |
vs = vehicle.dict()
|
33 |
+
vs["lat"] = vs["location_coordinates"][0]
|
34 |
+
vs["lon"] = vs["location_coordinates"][1]
|
35 |
return STATUS_TEMPLATE.format(**vs), vs
|
skills/weather.py
CHANGED
@@ -1,14 +1,19 @@
|
|
1 |
import requests
|
2 |
|
3 |
-
from .common import config
|
4 |
|
5 |
#current weather API
|
6 |
-
def get_weather(location:str= ""
|
7 |
"""
|
8 |
Returns the CURRENT weather in a specified location.
|
9 |
Args:
|
10 |
-
location (string) : Required. The name of the location, could be a city or lat/longitude in the following format latitude,longitude (example: 37.7749,-122.4194).
|
11 |
"""
|
|
|
|
|
|
|
|
|
|
|
12 |
# The endpoint URL provided by WeatherAPI
|
13 |
url = f"http://api.weatherapi.com/v1/current.json?key={config.WEATHER_API_KEY}&q={location}&aqi=no"
|
14 |
print(url)
|
|
|
1 |
import requests
|
2 |
|
3 |
+
from .common import config, vehicle
|
4 |
|
5 |
#current weather API
|
6 |
+
def get_weather(location:str= ""):
|
7 |
"""
|
8 |
Returns the CURRENT weather in a specified location.
|
9 |
Args:
|
10 |
+
location (string) : Required. The name of the location, could be a city or lat/longitude in the following format latitude,longitude (example: 37.7749,-122.4194). If the location is not specified, the function will return the weather in the current location.
|
11 |
"""
|
12 |
+
|
13 |
+
if location == "":
|
14 |
+
print(f"get_weather: location is empty, using the vehicle location. ({vehicle.location})")
|
15 |
+
location = vehicle.location
|
16 |
+
|
17 |
# The endpoint URL provided by WeatherAPI
|
18 |
url = f"http://api.weatherapi.com/v1/current.json?key={config.WEATHER_API_KEY}&q={location}&aqi=no"
|
19 |
print(url)
|