File size: 9,742 Bytes
014f597
1d0b9d1
89c9126
35aeee0
655dcc9
89c9126
44e58a6
 
c6542ed
6269c32
655dcc9
b421a2a
655dcc9
318bbdf
 
 
89c9126
 
318bbdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89c9126
6bdfadb
35aeee0
89c9126
 
18c04d1
e4d45f0
89c9126
1d0b9d1
89c9126
18c04d1
014f597
 
6bdfadb
35aeee0
89c9126
 
 
 
 
18c04d1
89c9126
 
6bdfadb
35aeee0
6bdfadb
 
18c04d1
 
6bdfadb
 
b139634
e4d45f0
6bdfadb
 
c6542ed
35aeee0
c6542ed
 
18c04d1
 
c6542ed
18c04d1
 
 
 
 
c6542ed
18c04d1
c6542ed
 
6269c32
35aeee0
6269c32
 
 
 
 
0f5d5f3
6269c32
6bdfadb
95e43e2
 
 
 
 
 
 
0f5d5f3
95e43e2
d07810d
d69ce5a
7a165bc
95e43e2
b421a2a
655dcc9
 
 
 
 
 
 
 
 
070bf41
655dcc9
 
 
 
070bf41
655dcc9
 
 
 
070bf41
655dcc9
 
 
 
070bf41
655dcc9
 
 
 
 
 
070bf41
655dcc9
 
 
 
 
070bf41
655dcc9
 
 
 
 
 
070bf41
655dcc9
 
 
d69ce5a
6bdfadb
97a44d4
b139634
44e58a6
b139634
 
c6542ed
6269c32
97a44d4
95e43e2
318bbdf
6bdfadb
89c9126
b139634
 
 
89c9126
 
 
1d0b9d1
89c9126
 
97a44d4
1d0b9d1
014f597
1d0b9d1
014f597
1d0b9d1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import gradio as gr

# Local modules
import fastf1_tools
import openf1_tools
from utils.constants import (
    DRIVER_NAMES,
    CONSTRUCTOR_NAMES,
    CURRENT_YEAR,
    DROPDOWN_SESSION_TYPES,
    MARKDOWN_INTRODUCTION,
    HTML_INTRODUCTION,
    MARKDOWN_OPENF1_EXAMPLES,
    OPENF1_TOOL_DESCRIPTION,
    CONSTRUCTORS_PER_SEASON,
    DRIVERS_PER_SEASON
)

with gr.Blocks() as iface_driver_championship_standings:
    gr.Markdown("## World Driver Championship Standings\nGet the world driver championship standings for a specific driver. Note that the older data has gaps and may not be entirely complete.")

    with gr.Row():
        year_input = gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR)
        driver_dropdown = gr.Dropdown(label="Driver", choices=DRIVERS_PER_SEASON.get(str(year_input.value), []))
    output_text = gr.Textbox(label="Result")
    submit_btn = gr.Button("Submit")

    def update_drivers(year):
        choices = DRIVERS_PER_SEASON.get(str(year), [])
        return gr.update(choices=choices, value=(choices[0] if choices else None))

    year_input.blur(
        update_drivers,
        inputs=year_input,
        outputs=driver_dropdown
    )
    submit_btn.click(
        fastf1_tools.driver_championship_standings,
        inputs=[year_input, driver_dropdown],
        outputs=output_text
    )

with gr.Blocks() as iface_constructor_championship_standings:
    gr.Markdown("## World Constructor Championship Standings\nGet the current/past world constructor championship standings for a specific constructor. Note that the older data has gaps and may not be entirely complete.")
    
    with gr.Row():
        year_input = gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR)
        constructor_dropdown = gr.Dropdown(label="Constructor", choices=CONSTRUCTORS_PER_SEASON.get(str(year_input.value), []))
    output_text = gr.Textbox(label="Result")
    submit_btn = gr.Button("Submit")

    def update_constructors(year):
        choices = CONSTRUCTORS_PER_SEASON.get(str(year), [])
        return gr.update(choices=choices, value=(choices[0] if choices else None))

    year_input.blur(
        update_constructors,
        inputs=year_input,
        outputs=constructor_dropdown
    )
    submit_btn.click(
        fastf1_tools.constructor_championship_standings,
        inputs=[year_input, constructor_dropdown],
        outputs=output_text
    )

iface_event_info = gr.Interface(
    fn=fastf1_tools.get_event_info,
    inputs=[
        gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
        gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
        gr.Radio(["human", "LLM"], label="Display format", value="human", info="Toggle between human-readable (parsed) and LLM output (raw)")
    ],
    outputs="text",
    title="Event Info",
    description="Get information about a specific Grand Prix event. Example: (2025,Monaco,human)"
)

iface_season_calendar = gr.Interface(
    fn=fastf1_tools.get_season_calendar,
    inputs=[
        gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
    ],
    outputs="text",
    title="Season Calendar",
    description="Get the season calendar for the given year"
)

iface_track_visualization = gr.Interface(
    fn=fastf1_tools.track_visualization,
    inputs=[
        gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
        gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
        gr.Radio(["speed", "corners", "gear"], label="Visualization type", value="speed", info="What type of track visualization to generate"),
    ],
    outputs="image",
    title="Track Visualizations",
    description="Get the track visualization (speed/corners/gear) for the fastest lap at the specific Grand Prix race. Example: (2025,Monaco,speed)"
)

iface_session_results = gr.Interface(
    fn=fastf1_tools.get_session_results,
    inputs=[
        gr.Number(label="Calendar year", value=CURRENT_YEAR, minimum=1950, maximum=CURRENT_YEAR),
        gr.Textbox(label="Grand Prix", placeholder="Ex: Monaco", info="The name of the GP/country/location (Fuzzy matching supported) or round number"),
        gr.Dropdown([session_type for session_type in DROPDOWN_SESSION_TYPES if "practice" not in session_type], label="Session type", value="race", info="The session type to get results for. Dataframe's columns vary depending on session type.")
    ],
    outputs=gr.Dataframe(
        headers=None,              # Let it infer from returned DataFrame
        row_count=(0, "dynamic"),  # Start empty, allow it to grow
        col_count=(0, "dynamic")   # Let columns adjust too
    ),
    title="Session Results",
    description="Get the session results for the given Grand Prix. Example: (2025,Monaco,qualifying)"
)

iface_driver_info = gr.Interface(
    fn=fastf1_tools.get_driver_info,
    inputs=[
        gr.Dropdown(label="Driver", choices=DRIVER_NAMES)
    ],
    outputs="text",
    title="Driver Info",
    description="Get background information about a specific driver from the 2025 Formula 1 season"
)

iface_constructor_info = gr.Interface(
    fn=fastf1_tools.get_constructor_info,
    inputs=[
        gr.Dropdown(label="Constructor", choices=CONSTRUCTOR_NAMES)
    ],
    outputs="text",
    title="Constructor Info",
    description="Get background information about a specific constructor from the 2025 Formula 1 season"
)


# About introduction tab
with gr.Blocks() as markdown_tab:
    gr.HTML(HTML_INTRODUCTION)


# OpenF1 tools tab
def openf1_tools_tab():
    with gr.Blocks() as openf1_tools_tab:
        gr.Markdown(OPENF1_TOOL_DESCRIPTION)
        with gr.Accordion("get_api_endpoints()", open=False):
            btn = gr.Button("Get all endpoints")
            output = gr.JSON()
            btn.click(openf1_tools.get_api_endpoints, outputs=output)
        with gr.Accordion("get_api_endpoint(endpoint)", open=False):
            endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
            btn = gr.Button("Get endpoint info")
            output = gr.JSON()
            btn.click(openf1_tools.get_api_endpoint, inputs=endpoint_in, outputs=output)
        with gr.Accordion("get_endpoint_info(endpoint)", open=False):
            endpoint_in = gr.Textbox(label="Endpoint", placeholder="e.g. sessions")
            btn = gr.Button("Get endpoint details")
            output = gr.JSON()
            btn.click(openf1_tools.get_endpoint_info, inputs=endpoint_in, outputs=output)
        with gr.Accordion("get_filter_info(filter_name)", open=False):
            filter_in = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
            btn = gr.Button("Get filter info")
            output = gr.JSON()
            btn.click(openf1_tools.get_filter_info, inputs=filter_in, outputs=output)
        with gr.Accordion("get_filter_string(filter_name, filter_value, operator)", open=False):
            filter_name = gr.Textbox(label="Filter name", placeholder="e.g. driver_number")
            filter_value = gr.Textbox(label="Filter value", placeholder="e.g. 16")
            operator = gr.Dropdown(label="Operator", choices=["=", ">", "<", ">=", "<="], value="=")
            btn = gr.Button("Get filter string")
            output = gr.Textbox(label="Filter string", info="Example: driver_number=16&")
            btn.click(openf1_tools.get_filter_string, inputs=[filter_name, filter_value, operator], outputs=output)
        with gr.Accordion("apply_filters(api_string, *filters)", open=False):
            api_string = gr.Textbox(label="Base API string", placeholder="e.g. https://api.openf1.org/v1/sessions?")
            filters = gr.Textbox(label="Filters (comma-separated)", placeholder="e.g. driver_number=16&,session_key=123&")
            btn = gr.Button("Apply filters")
            output = gr.Textbox(label="Full API string")
            btn.click(openf1_tools.apply_filters, inputs=[api_string, filters], outputs=output)
        with gr.Accordion("send_request(api_string)", open=False):
            with gr.Accordion("Example API requests (copy & paste into text box below)", open=False):
                gr.Markdown(MARKDOWN_OPENF1_EXAMPLES)
            api_string = gr.Textbox(label="Full API string", placeholder="e.g. https://api.openf1.org/v1/sessions?driver_number=16")
            btn = gr.Button("Send API request")
            output = gr.JSON()
            btn.click(openf1_tools.send_request, inputs=api_string, outputs=output)
    return openf1_tools_tab

# OpenF1 tabs

named_interfaces = {
    "About": markdown_tab,
    "Driver Championship Standings": iface_driver_championship_standings,
    "Constructor Championship Standings": iface_constructor_championship_standings,
    "Event Info": iface_event_info,
    "Season Calendar": iface_season_calendar,
    "Track Visualizations": iface_track_visualization,
    "Session Results": iface_session_results,
    "Driver Info": iface_driver_info,
    "Constructor Info": iface_constructor_info,
    "OpenF1 Tools": openf1_tools_tab()
}

# Tab names and interfaces
tab_names = list(named_interfaces.keys())
interface_list = list(named_interfaces.values())


# Combine all the interfaces into a single TabbedInterface
gradio_server = gr.TabbedInterface(
    interface_list,
    tab_names=tab_names,
    title="๐Ÿ Formula 1 MCP server ๐ŸŽ๏ธ"
)

# Launch the interface and MCP server
if __name__ == "__main__":
    gradio_server.launch(mcp_server=True)