Spaces:
Sleeping
Sleeping
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 |
+
"""
|