arre99 commited on
Commit
655dcc9
·
1 Parent(s): 18c04d1

added tools for OpenF1 using tabs and blocks. Alot of examples of the different tools are provided, although the UI is not super user-friendly (expected due to is being pure API strings)

Browse files
Files changed (3) hide show
  1. app.py +73 -3
  2. todo.txt +1 -2
  3. utils/constants.py +35 -0
app.py CHANGED
@@ -1,14 +1,16 @@
1
  import gradio as gr
2
- import pandas as pd
3
 
4
  # Local modules
5
  import fastf1_tools
 
6
  from utils.constants import (
7
  DRIVER_NAMES,
8
  CONSTRUCTOR_NAMES,
9
  CURRENT_YEAR,
10
  DROPDOWN_SESSION_TYPES,
11
- MARKDOWN_INTRODUCTION
 
 
12
  )
13
 
14
  iface_driver_championship_standings = gr.Interface(
@@ -108,7 +110,74 @@ iface_constructor_info = gr.Interface(
108
  # Create your markdown-only tab using Blocks
109
  with gr.Blocks() as markdown_tab:
110
  gr.Markdown(MARKDOWN_INTRODUCTION)
111
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  named_interfaces = {
114
  "About": markdown_tab,
@@ -120,6 +189,7 @@ named_interfaces = {
120
  "Session Results": iface_session_results,
121
  "Driver Info": iface_driver_info,
122
  "Constructor Info": iface_constructor_info,
 
123
  }
124
 
125
  # Tab names and interfaces
 
1
  import gradio as gr
 
2
 
3
  # Local modules
4
  import fastf1_tools
5
+ import openf1_tools
6
  from utils.constants import (
7
  DRIVER_NAMES,
8
  CONSTRUCTOR_NAMES,
9
  CURRENT_YEAR,
10
  DROPDOWN_SESSION_TYPES,
11
+ MARKDOWN_INTRODUCTION,
12
+ MARKDOWN_OPENF1_EXAMPLES,
13
+ OPENF1_TOOL_DESCRIPTION
14
  )
15
 
16
  iface_driver_championship_standings = gr.Interface(
 
110
  # Create your markdown-only tab using Blocks
111
  with gr.Blocks() as markdown_tab:
112
  gr.Markdown(MARKDOWN_INTRODUCTION)
113
+
114
+
115
+
116
+ # OpenF1 tools tab
117
+ def openf1_tools_tab():
118
+ with gr.Blocks() as openf1_tools_tab:
119
+ gr.Markdown(OPENF1_TOOL_DESCRIPTION)
120
+ with gr.Accordion("get_api_endpoints()", open=False):
121
+ btn = gr.Button("Get all endpoints")
122
+ output = gr.JSON()
123
+ def call():
124
+ return openf1_tools.get_api_endpoints()
125
+ btn.click(call, outputs=output)
126
+ with gr.Accordion("get_api_endpoint(endpoint)", open=False):
127
+ endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
128
+ btn = gr.Button("Get endpoint info")
129
+ output = gr.JSON()
130
+ def call(endpoint):
131
+ return openf1_tools.get_api_endpoint(endpoint)
132
+ btn.click(call, inputs=endpoint_in, outputs=output)
133
+ with gr.Accordion("get_endpoint_info(endpoint)", open=False):
134
+ endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
135
+ btn = gr.Button("Get endpoint details")
136
+ output = gr.JSON()
137
+ def call(endpoint):
138
+ return openf1_tools.get_endpoint_info(endpoint)
139
+ btn.click(call, inputs=endpoint_in, outputs=output)
140
+ with gr.Accordion("get_filter_info(filter_name)", open=False):
141
+ filter_in = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
142
+ btn = gr.Button("Get filter info")
143
+ output = gr.JSON()
144
+ def call(filter_name):
145
+ return openf1_tools.get_filter_info(filter_name)
146
+ btn.click(call, inputs=filter_in, outputs=output)
147
+ with gr.Accordion("get_filter_string(filter_name, filter_value, operator)", open=False):
148
+ filter_name = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
149
+ filter_value = gr.Textbox(label="Filter value", placeholder="e.g. 16")
150
+ operator = gr.Dropdown(label="Operator", choices=["=", ">", "<", ">=", "<="], value="=")
151
+ btn = gr.Button("Get filter string")
152
+ output = gr.Textbox(label="Filter string", info="Example: driver_number=16&")
153
+ def call(filter_name, filter_value, operator):
154
+ return openf1_tools.get_filter_string(filter_name, filter_value, operator)
155
+ btn.click(call, inputs=[filter_name, filter_value, operator], outputs=output)
156
+ with gr.Accordion("apply_filters(api_string, *filters)", open=False):
157
+ api_string = gr.Textbox(label="Base API string", placeholder="e.g. https://api.openf1.org/v1/sessions?")
158
+ filters = gr.Textbox(label="Filters (comma-separated)", placeholder="e.g. driver_number=16&,session_key=123&")
159
+ btn = gr.Button("Apply filters")
160
+ output = gr.Textbox(label="Full API string")
161
+ def call(api_string, filters):
162
+ # Expect filters as comma-separated
163
+ filter_list = [f.strip() for f in filters.split(",") if f.strip()]
164
+ return openf1_tools.apply_filters(api_string, *filter_list)
165
+ btn.click(call, inputs=[api_string, filters], outputs=output)
166
+ with gr.Accordion("send_request(api_string)", open=False):
167
+ with gr.Accordion("Example API requests (copy & paste into text box below)", open=False):
168
+ gr.Markdown(MARKDOWN_OPENF1_EXAMPLES)
169
+ api_string = gr.Textbox(label="Full API string", placeholder="e.g. https://api.openf1.org/v1/sessions?driver_number=16")
170
+ btn = gr.Button("Send API request")
171
+ output = gr.JSON()
172
+ def call(api_string):
173
+ try:
174
+ return openf1_tools.send_request(api_string)
175
+ except Exception as e:
176
+ return {"error": str(e)}
177
+ btn.click(call, inputs=api_string, outputs=output)
178
+ return openf1_tools_tab
179
+
180
+ # OpenF1 tabs
181
 
182
  named_interfaces = {
183
  "About": markdown_tab,
 
189
  "Session Results": iface_session_results,
190
  "Driver Info": iface_driver_info,
191
  "Constructor Info": iface_constructor_info,
192
+ "OpenF1 Tools": openf1_tools_tab(),
193
  }
194
 
195
  # Tab names and interfaces
todo.txt CHANGED
@@ -1,5 +1,4 @@
1
  - For driver championship standings, make the Driver dropdown depend on the selected season. As it is implemented now one could not use it for older seasons!
2
  * Solution is to have a static json file that maps drivers for each season/year
3
  - Same applies for constructor championship standings but instead Constructor dropdown
4
- * Similar solution to above
5
- - Implement a tab for the OpenF1 tools but it might not be that user friendly :/
 
1
  - For driver championship standings, make the Driver dropdown depend on the selected season. As it is implemented now one could not use it for older seasons!
2
  * Solution is to have a static json file that maps drivers for each season/year
3
  - Same applies for constructor championship standings but instead Constructor dropdown
4
+ * Similar solution to above
 
utils/constants.py CHANGED
@@ -94,3 +94,38 @@ For Claude Desktop, the following configuration can instead be used, but make su
94
  }
95
  ```
96
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
  ```
96
  """
97
+
98
+
99
+ OPENF1_TOOL_DESCRIPTION = """
100
+ ## OpenF1 Tools - API Endpoints.
101
+
102
+ This UI Interface/Tab collects all the MCP tools that are based on the `OpenF1` API, which are a bit more advanced compared to the other UI tabs that are implemented using the FastF1 library.
103
+ In essence, the tools listed below make it possible to access the `OpenF1` API directly within the MCP server, thus allowing a LLM to interact with the `OpenF1` API.
104
+ The **_OpenF1_** API exposes several **_endpoints_** that can be used to access different types of real-time and historical data about Formula 1 races, drivers, and teams.
105
+ Each of these endpoints have different **_filters_** that can be used to filter the data returned by the endpoint. The data passed and returned is entirely in JSON format.
106
+
107
+ The implemented functions make it possible to:
108
+ - Get all available endpoints - `get_api_endpoints()`
109
+ - Get information about a specific endpoint - `get_api_endpoint(endpoint)`
110
+ - Get information about a specific filter - `get_filter_info(filter_name)`
111
+ - Get a filter string for a specific filter - `get_filter_string(filter_name, filter_value, operator)`
112
+ - Apply filters to an API string - `apply_filters(api_string, *filters)`
113
+ - Send a request to the OpenF1 API - `send_request(api_string)`
114
+
115
+ """
116
+
117
+ MARKDOWN_OPENF1_EXAMPLES = """
118
+
119
+ ```https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315```
120
+
121
+ ```https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158```
122
+
123
+ ```https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005```
124
+
125
+ ```https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8```
126
+
127
+ ```https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore```
128
+
129
+ ```https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31```
130
+
131
+ """