Wire the get_weather tool into the weather assistant with idempotent execution

Python · LLM apps · advanced · greenfield

Wires the weather assistant to the `get_weather` tool: the model gets the tool declaration, and when it asks for a call the endpoint validates the produced arguments against the declared schema, runs the lookup and feeds the result back per the tool-call contract — assistant message first, each result with its matching `tool_call_id` — looping until a final answer or the 5-round cap. Duplicate `tool_call_id` values are refused so a re-emitted call can't run the tool twice. Invalid arguments go back to the model as structured errors instead of crashing the request.

A customer-facing weather bot. The model decides when to call the tool and with what arguments. The tool is a mock — it returns canned weather data — but the surrounding validation, message ordering, and idempotency logic is production-grade. A real `get_weather` would call a paid external API, so double-execution is a real cost and correctness concern.

Requirements

Files touched

--- app/weather_tools.py
+"""Weather tool: the schema declaration and the mock implementation."""
+
+from __future__ import annotations
+
+from typing import Any
+
+# Tool declaration sent with every model call.
+# The declared schema is also the validation contract for model-produced arguments.
+WEATHER_TOOL: dict[str, Any] = {
+    "type": "function",
+    "function": {
+        "name": "get_weather",
+        "description": (
+            "Get the current weather and optionally a multi-day forecast "
+            "for a location."
+        ),
+        "parameters": {
+            "type": "object",
+            "properties": {
+                "location": {
+                    "type": "string",
+                    "minLength": 1,
+                    "description": "City name, e.g. 'London' or 'Tokyo'.",
+                },
+                "units": {
+                    "type": "string",
+                    "enum": ["celsius", "fahrenheit"],
+                    "default": "celsius",
+                    "description": "Temperature unit.",

Review this PR

Python practice