florentgbelidji HF staff commited on
Commit
0c78cd5
·
1 Parent(s): aaf477c

Added main file of the gradio app

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import pandas as pd
4
+ import numpy as np
5
+ import gradio as gr
6
+ from gradio_folium import Folium
7
+ from smolagents import CodeAgent, LiteLLMModel, HfApiModel
8
+ from src.gradio_utils import ( create_map_from_markers,
9
+ update_map_on_selection,
10
+ stream_to_gradio,
11
+ interact_with_agent,
12
+ toggle_visibility,
13
+ FINAL_MESSAGE_HEADER,
14
+ MAP_URL)
15
+
16
+ from src.prompts import SKI_TOURING_ASSISTANT_PROMPT
17
+ from src.tools import (RefugeTool,
18
+ MountainRangesTool,
19
+ ForecastTool,
20
+ GetRoutesTool,
21
+ DescribeRouteTool)
22
+ from folium import Map, TileLayer, Marker, Icon
23
+ from dotenv import load_dotenv
24
+
25
+ # Load environment variables
26
+ load_dotenv()
27
+
28
+
29
+ required_variables = [
30
+ "HF_TOKEN",
31
+ "GOOGLE_MAPS_API_KEY",
32
+ "SKITOUR_API_TOKEN",
33
+ "METEO_FRANCE_API_TOKEN",
34
+ "HUGGINGFACE_ENDPOINT_ID_QWEN"
35
+ ]
36
+
37
+ # Find missing variables
38
+ missing_variables = [var for var in required_variables if var not in os.environ]
39
+
40
+ if missing_variables:
41
+ raise EnvironmentError(f"Missing required environment variables: {', '.join(missing_variables)}")
42
+
43
+ print("All required variables are set.")
44
+
45
+ # Load the summit clusters
46
+ # Useful for assigning locations to mountain ranges
47
+ with open("data/summit_clusters.json", "r") as f:
48
+ summit_clusters = json.load(f)
49
+
50
+ with open("data/skitour2mf_lookup.json", "r") as f:
51
+ skitour2mf_lookup = json.load(f)
52
+
53
+ def get_tools(llm_engine):
54
+ refuge_tool = RefugeTool()
55
+ mountain_ranges_tool = MountainRangesTool(summit_clusters)
56
+ forecast_tool = ForecastTool(
57
+ llm_engine=llm_engine,
58
+ clusters=summit_clusters,
59
+ skitour2meteofrance=skitour2mf_lookup
60
+ )
61
+ get_routes_tool = GetRoutesTool()
62
+ description_route_tool = DescribeRouteTool(
63
+ skitour2meteofrance=skitour2mf_lookup,
64
+ llm_engine=llm_engine
65
+ )
66
+ return [mountain_ranges_tool, forecast_tool, get_routes_tool, description_route_tool]
67
+
68
+ # Initialize the default agent
69
+ def init_default_agent(llm_engine):
70
+ return CodeAgent(
71
+ tools = get_tools(llm_engine),
72
+ model = llm_engine,
73
+ additional_authorized_imports=["pandas"],
74
+ max_steps=20,
75
+ )
76
+
77
+ # Initialize the default agent prompt
78
+ def init_default_agent_prompt():
79
+ return {"specific_agent_role_prompt": SKI_TOURING_ASSISTANT_PROMPT.format(language="French")}
80
+
81
+ def create_llm_engine(type_engine: str, api_key: str = None):
82
+ if type_engine == "openai/gpt-4o" and api_key:
83
+ llm_engine = LiteLLMModel(model_id="openai/gpt-4o", api_key=api_key)
84
+ return llm_engine
85
+ elif type_engine == "openai/gpt-4o" and not api_key:
86
+ raise ValueError("You need to provide an API key to use the the model engine.")
87
+ elif type_engine == "Qwen/Qwen2.5-Coder-32B-Instruct":
88
+ llm_engine = HfApiModel(model_id=os.environ["HUGGINGFACE_ENDPOINT_ID_QWEN"])
89
+ return llm_engine
90
+ elif type_engine == "meta-llama/Llama-3.3-70B-Instruct":
91
+ llm_engine = HfApiModel(model_id=os.environ["HUGGINGFACE_ENDPOINT_ID_LLAMA"])
92
+ return llm_engine
93
+ else:
94
+ raise ValueError("Invalid engine type. Please choose either 'openai/gpt-4o' or 'Qwen/Qwen2.5-Coder-32B-Instruct'.")
95
+
96
+ def initialize_new_agent(engine_type, api_key):
97
+ try:
98
+ llm_engine = create_llm_engine(engine_type, api_key)
99
+ tools = get_tools(llm_engine)
100
+ skier_agent = CodeAgent(
101
+ tools = tools,
102
+ model = llm_engine,
103
+ additional_authorized_imports=["pandas"],
104
+ max_steps=20,
105
+ )
106
+ return skier_agent, [], gr.Chatbot([], label="Agent Thoughts", type="messages")
107
+ except ValueError as e:
108
+ return str(e)
109
+
110
+ # Sample data for demonstration
111
+ sample_data = {
112
+ "id": [0],
113
+ "Name": "Mont Blanc, Par les Grands Mulets",
114
+ "Latitude": [45.90181],
115
+ "Longitude": [6.86153],
116
+ "Route Link": ["https://skitour.fr/topos/770"],
117
+ }
118
+ df_sample_routes = pd.DataFrame(sample_data)
119
+
120
+ # Default engine
121
+ if os.environ.get("OPENAI_API_KEY"):
122
+ default_engine = create_llm_engine("openai/gpt-4o", os.environ.get("OPENAI_API_KEY"))
123
+ else:
124
+ default_engine = create_llm_engine("Qwen/Qwen2.5-Coder-32B-Instruct")
125
+
126
+
127
+ # Gradio UI
128
+ def build_ui():
129
+ with gr.Blocks() as demo:
130
+ gr.Markdown("<center><h1>Ski Touring Agent Planner</h1></center>")
131
+
132
+ gr.Image(value="./data/skitourai.jpeg", height=400, width=400)
133
+ with gr.Accordion("About the App❓", open=False):
134
+ gr.Markdown("""
135
+ **🇬🇧 English Version**
136
+
137
+ The Ski Touring Assistant is built with the **[Smolagents](https://github.com/huggingface/smolagents) library by Hugging Face** and relies on data from [Skitour.fr](https://skitour.fr) and [Météo France - Montagne](https://meteofrance.com/meteo-montagne).
138
+ It is designed specifically to help plan ski touring routes **in the Alps and the Pyrenees, in France only**. While the app provides AI-generated suggestions, it is essential to **always verify snow and avalanche conditions directly on Météo-France for your safety.**
139
+
140
+ #### Key Features
141
+
142
+ - **Interactive Maps**: Plan routes with data from [Skitour.fr](https://skitour.fr), covering ski touring trails in the Alps and the Pyrenees.
143
+ - **AI Assistance**: Get route recommendations, hazard insights, and metrics like elevation and travel time.
144
+ - **Snow & Avalanche Conditions**: Access real-time information via [Météo-France](https://meteofrance.com/meteo-montagne).
145
+ - **Multilingual Support**: Available in English and French.
146
+
147
+ Enjoy your ski touring adventures in France, but always double-check official sources for safety!
148
+
149
+ ---
150
+ **🇫🇷 Version Française**
151
+
152
+ L'assistant de ski de randonnée est construit avec la bibliothèque **[Smolagents](https://github.com/huggingface/smolagents) de Hugging Face** et repose sur les données de [Skitour.fr](https://skitour.fr) et [Météo France - Montagne](https://meteofrance.com/meteo-montagne).
153
+ Il est conçu spécifiquement pour aider à planifier des itinéraires de ski de randonnée **dans les Alpes et les Pyrénées, uniquement en France**. Bien que l'application fournisse des suggestions générées par IA, il est essentiel de **toujours vérifier la météo et le bulletin d'estimation des risques d'avalanche (BERA) directement sur Météo-France pour votre sécurité**.
154
+
155
+ #### Principales Fonctionnalités
156
+
157
+ - **Cartes interactives** : Planifiez des itinéraires avec des données de [Skitour.fr](https://skitour.fr), couvrant les sentiers de ski de randonnée dans les Alpes et les Pyrénées.
158
+ - **Assistance IA** : Obtenez des recommandations d'itinéraires, des informations sur les risques et des métriques comme l'altitude et le temps de trajet.
159
+ - **Conditions de neige et d'avalanche** : Accédez à des informations en temps réel via [Météo-France](https://meteofrance.com/meteo-montagne).
160
+ - **Support multilingue** : Disponible en anglais et en français.
161
+
162
+ Profitez de vos aventures en ski de randonnée en France, mais vérifiez toujours les sources officielles pour votre sécurité !
163
+ """, container=True)
164
+
165
+
166
+ skier_agent = gr.State(lambda: init_default_agent(default_engine))
167
+ with gr.Row():
168
+ with gr.Column():
169
+ language = gr.Radio(["English", "French"], value="French", label="Language")
170
+ skier_agent_prompt = gr.State(init_default_agent_prompt)
171
+ language_button = gr.Button("Update language")
172
+ model_type = gr.Dropdown(choices = ["Qwen/Qwen2.5-Coder-32B-Instruct", "meta-llama/Llama-3.3-70B-Instruct", "openai/gpt-4o", ],
173
+ value="Qwen/Qwen2.5-Coder-32B-Instruct",
174
+ label="Model Type",
175
+ info="If you choose openai/gpt-4o, you need to provide an API key.",
176
+ interactive=True
177
+ )
178
+ api_key_textbox = gr.Textbox(label="API Key", placeholder="Enter your API key", type="password", visible=False)
179
+
180
+
181
+ model_type.change(
182
+ lambda x: toggle_visibility(True) if x =='openai/gpt-4o' else toggle_visibility(False),
183
+ [model_type],
184
+ [api_key_textbox]
185
+ )
186
+ update_engine = gr.Button("Update LLM Engine")
187
+
188
+
189
+ stored_message = gr.State([])
190
+ chatbot = gr.Chatbot(label="Agent Thoughts", type="messages")
191
+ warning = gr.Warning("The agent can take few seconds to minutes to respond.", visible=True)
192
+ text_output = gr.Markdown(value=FINAL_MESSAGE_HEADER, container=True)
193
+ warning = gr.Markdown("⚠️ The agent can take few seconds to minutes to respond.", container=True)
194
+ text_input = gr.Textbox(lines=1, label="Chat Message", submit_btn=True)
195
+ gr.Examples(["Can you provide an itinerary near Grenoble?"], text_input)
196
+
197
+ with gr.Column():
198
+ f_map = Folium(value=Map(
199
+ location=[45.9237, 6.8694],
200
+ zoom_start=10,
201
+ tiles= TileLayer(
202
+ tiles=MAP_URL,
203
+ attr="Google",
204
+ name="Google Maps",
205
+ overlay=True,
206
+ control=True )
207
+ )
208
+ )
209
+
210
+ df_routes = gr.State(pd.DataFrame(df_sample_routes))
211
+ data = gr.DataFrame(value=df_routes.value[["Name", "Route Link"]], datatype="markdown", interactive=False)
212
+
213
+ language_button.click(lambda s: {"specific_agent_role_prompt": SKI_TOURING_ASSISTANT_PROMPT.format(language=s)}, [language], [skier_agent_prompt])
214
+ update_engine.click(
215
+ fn=initialize_new_agent,
216
+ inputs=[model_type, api_key_textbox],
217
+ outputs=[skier_agent, stored_message, chatbot]
218
+ )
219
+
220
+ text_input.submit(lambda s: (s, ""), [text_input], [stored_message, text_input]) \
221
+ .then(interact_with_agent, [skier_agent, stored_message, chatbot, df_routes, skier_agent_prompt], [chatbot, df_routes, text_output])
222
+
223
+ df_routes.change(create_map_from_markers, [df_routes], [f_map]).then(lambda s: gr.DataFrame(s[["Name", "Route Link"]], datatype="markdown", interactive=False), [df_routes], [data])
224
+ data.select(
225
+ update_map_on_selection, [data, df_routes],[f_map]
226
+ )
227
+
228
+ demo.launch()
229
+
230
+ # Launch the app
231
+ if __name__ == "__main__":
232
+ build_ui()