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- app.py +73 -3
- todo.txt +1 -2
- 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 |
+
"""
|