Weather Agent Ollama

Weather agent ollama example.

Weather Agent using Flux agent() with Tool Use.

Run it

python examples/ai/weather_agent_ollama.py
"""
Weather Agent using Flux agent() with Tool Use.

This example demonstrates the agent() primitive with tool calling — the agent
autonomously decides when to call weather tools to answer user questions.

Compare with:
- examples/ai/function_calling_agent_ollama.py (manual tool loop, ~400 lines)

The agent() version replaces the manual tool-use loop, message management, and
LLM interaction code with a single agent() call. The tools are existing Flux
@task functions — their signature and docstring are used to generate tool schemas
automatically.

Prerequisites:
    1. Install Ollama: https://ollama.ai
    2. Pull a model that supports tools: ollama pull llama3.2
    3. Start Ollama service: ollama serve

Usage:
    flux workflow run weather_agent_ollama '{"question": "What is the weather in San Francisco?"}'
    flux workflow run weather_agent_ollama '{"question": "Compare weather in Tokyo and London"}'
"""

from __future__ import annotations

from typing import Any

import httpx

from flux import ExecutionContext, task, workflow
from flux.tasks.ai import agent


@task.with_options(retry_max_attempts=3, retry_delay=1, retry_backoff=2, timeout=30)
async def get_current_weather(location: str) -> str:
    """Get the current weather for a city. Use this when the user asks about current weather, temperature, or conditions."""
    try:
        async with httpx.AsyncClient() as client:
            geo = await client.get(
                "https://geocoding-api.open-meteo.com/v1/search",
                params={"name": location, "count": 1, "language": "en", "format": "json"},
            )
            geo.raise_for_status()
            geo_data = geo.json()

            if not geo_data.get("results"):
                return f"Location not found: {location}"

            result = geo_data["results"][0]
            lat, lon = result["latitude"], result["longitude"]
            name = f"{result['name']}, {result.get('country', '')}"

            weather = await client.get(
                "https://api.open-meteo.com/v1/forecast",
                params={
                    "latitude": lat,
                    "longitude": lon,
                    "current": "temperature_2m,relative_humidity_2m,apparent_temperature,wind_speed_10m,weather_code",
                    "temperature_unit": "fahrenheit",
                    "wind_speed_unit": "mph",
                },
            )
            weather.raise_for_status()
            current = weather.json()["current"]

            codes = {
                0: "Clear",
                1: "Mainly clear",
                2: "Partly cloudy",
                3: "Overcast",
                45: "Foggy",
                51: "Light drizzle",
                61: "Light rain",
                63: "Rain",
                65: "Heavy rain",
                71: "Light snow",
                73: "Snow",
                95: "Thunderstorm",
            }

            return (
                f"Weather in {name}: {current['temperature_2m']}°F "
                f"(feels like {current['apparent_temperature']}°F), "
                f"{codes.get(current['weather_code'], 'Unknown conditions')}, "
                f"humidity {current['relative_humidity_2m']}%, "
                f"wind {current['wind_speed_10m']} mph"
            )
    except Exception as e:
        return f"Failed to get weather for '{location}': {e}"


@task.with_options(retry_max_attempts=3, retry_delay=1, retry_backoff=2, timeout=30)
async def get_weather_forecast(location: str, days: int = 3) -> str:
    """Get the weather forecast for the next several days. Use this when the user asks about future weather or forecasts."""
    try:
        days = max(1, min(days, 7))
        async with httpx.AsyncClient() as client:
            geo = await client.get(
                "https://geocoding-api.open-meteo.com/v1/search",
                params={"name": location, "count": 1, "language": "en", "format": "json"},
            )
            geo.raise_for_status()
            geo_data = geo.json()

            if not geo_data.get("results"):
                return f"Location not found: {location}"

            result = geo_data["results"][0]
            name = f"{result['name']}, {result.get('country', '')}"

            weather = await client.get(
                "https://api.open-meteo.com/v1/forecast",
                params={
                    "latitude": result["latitude"],
                    "longitude": result["longitude"],
                    "daily": "temperature_2m_max,temperature_2m_min,weather_code",
                    "temperature_unit": "fahrenheit",
                    "forecast_days": days,
                },
            )
            weather.raise_for_status()
            daily = weather.json()["daily"]

            lines = [f"Forecast for {name}:"]
            for i in range(len(daily["time"])):
                lines.append(
                    f"  {daily['time'][i]}: {daily['temperature_2m_min'][i]}°F - {daily['temperature_2m_max'][i]}°F",
                )
            return "\n".join(lines)
    except Exception as e:
        return f"Failed to get forecast for '{location}': {e}"


@workflow
async def weather_agent_ollama(ctx: ExecutionContext[dict[str, Any]]):
    """
    Weather agent using Flux agent() with tool calling.

    The agent autonomously decides which weather tools to call based on the question.

    Input format:
    {
        "question": "What's the weather in San Francisco?"
    }
    """
    input_data = ctx.input or {}
    question = input_data.get("question")
    if not question:
        return {"error": "Missing required parameter 'question'", "execution_id": ctx.execution_id}

    weather_assistant = await agent(
        "You are a helpful weather assistant. Use the available tools to look up "
        "current weather conditions and forecasts. Always provide clear, concise answers.",
        model="ollama/llama3.2",
        name="weather_assistant",
        tools=[get_current_weather, get_weather_forecast],
    )

    answer = await weather_assistant(question)

    return {
        "question": question,
        "answer": answer,
        "execution_id": ctx.execution_id,
    }


if __name__ == "__main__":  # pragma: no cover
    questions = [
        "What's the current weather in San Francisco?",
        "Give me the 3-day forecast for Tokyo",
    ]

    for question in questions:
        try:
            print("=" * 80)
            print(f"Q: {question}")
            print("=" * 80)

            result = weather_agent_ollama.run({"question": question})

            if result.has_failed:
                raise Exception(f"Workflow failed: {result.output}")

            print(f"\nA: {result.output['answer']}\n")

        except Exception as e:
            print(f"Error: {e}")
            print("Make sure Ollama is running: ollama serve")
            print("And model is pulled: ollama pull llama3.2\n")

Last verified against Flux 0.56.0.