"""Gradio front end for Commute Copilot (this is the Hugging Face Space entry point).""" import os import gradio as gr from commute_copilot import CommuteConfig, ask, build_config from commute_copilot.config import LINES, line_stations DEFAULT = CommuteConfig() EXAMPLES = [ "I need to head downtown. When should I leave, and do I need a jacket?", "Is anything wrong with the trains right now?", "Should I bring an umbrella this afternoon?", ] def station_choices(route: str): try: return [(name, stop_id) for stop_id, name, _, _ in line_stations(route)] except Exception: return [(DEFAULT.stop_name, DEFAULT.stop_id)] if route == DEFAULT.route else [] def on_line_change(route): choices = station_choices(route) value = DEFAULT.stop_id if route == DEFAULT.route else (choices[0][1] if choices else None) return gr.Dropdown(choices=choices, value=value) def respond(message, history, route, stop_id, walk_minutes, mode): if not os.environ.get("OPENROUTER_API_KEY"): return ("OPENROUTER_API_KEY is not set. On Hugging Face, add it under Settings -> " "Variables and secrets, then restart the Space.", "") try: cfg = (DEFAULT if (route, stop_id, int(walk_minutes)) == (DEFAULT.route, DEFAULT.stop_id, DEFAULT.walk_minutes) else build_config(route, stop_id, walk_minutes)) except Exception as e: return f"Could not load that station from the MBTA ({e}).", "" try: result = ask(message, cfg, mode="multi" if mode.startswith("3") else "single", history=history) except Exception as e: # Most often a 429 from the free OpenRouter tier (20/min, 50/day) return f"The model call failed: {type(e).__name__}: {e}", "" answer = result.answer if result.invented_times: answer += ("\n\n⚠️ Guardrail 3: these times did not come from live data, so treat " f"them as unverified: {', '.join(result.invented_times)}") return answer, result.summary() with gr.Blocks(title="Commute Copilot") as demo: gr.Markdown( "# 🚇 Commute Copilot\n" "Tells you **when to walk out the door** for the next MBTA train and **whether the " "weather changes the plan**. Uses live MBTA predictions, MBTA service alerts and " "Open-Meteo forecasts. Built with LangChain and LangGraph.") with gr.Row(): line = gr.Dropdown(LINES, value=DEFAULT.route, label="Line") station = gr.Dropdown(station_choices(DEFAULT.route), value=DEFAULT.stop_id, label="Station") walk = gr.Slider(0, 30, value=DEFAULT.walk_minutes, step=1, label="Walk to platform (min)") mode = gr.Radio(["Single agent", "3-agent graph"], value="Single agent", label="Architecture") line.change(on_line_change, line, station) details = gr.Textbox(label="Run details: ReAct trace, tokens, guardrails", lines=14, render=False) gr.ChatInterface( respond, additional_inputs=[line, station, walk, mode], additional_outputs=[details], examples=[[q] for q in EXAMPLES], cache_examples=False, ) with gr.Accordion("Run details", open=False): details.render() if __name__ == "__main__": demo.launch()