# `ADK.Tool.LongRunningTool`
[🔗](https://github.com/zeroasterisk/adk-elixir/blob/main/lib/adk/tool/long_running_tool.ex#L1)

An asynchronous tool that executes work in a supervised BEAM process.

Long-running tools spawn a Task under the `ADK.RunnerSupervisor`,
allowing the tool function to send intermediate status updates while
the caller awaits completion. This is the BEAM-idiomatic equivalent
of Python ADK's `LongRunningFunctionTool`.

## Key differences from Python ADK

Python ADK marks tools with `is_long_running = True` and relies on
async/await coroutines. In Elixir, we use OTP processes:

- Work runs in a supervised `Task` (fault-tolerant, isolated)
- Status updates flow via process messages
- Timeout is enforced via `receive...after`
- Crashes in the tool are caught and returned as `{:error, reason}`

## Usage

    tool = ADK.Tool.LongRunningTool.new(:fetch_report,
      description: "Fetch and process a large report",
      func: fn _ctx, %{"url" => url}, send_update ->
        send_update.("Connecting to #{url}...")
        data = fetch_data(url)
        send_update.("Processing #{byte_size(data)} bytes...")
        process(data)
      end,
      parameters: %{
        type: "object",
        properties: %{url: %{type: "string", description: "Report URL"}},
        required: ["url"]
      },
      timeout: 30_000
    )

## Function signature

The `func` must accept 3 arguments:

1. `tool_ctx` — `ADK.ToolContext.t()` (same as regular tools)
2. `args` — `map()` of tool arguments from the LLM
3. `send_update` — `(String.t() -> :ok)` callback to emit status updates

## Return value

- `{:ok, result}` — success with final result
- `{:ok, %{result: result, status_updates: [String.t()]}}` — success with updates captured
- `{:error, reason}` — tool error, timeout, or crash

## Description annotation

The tool description is automatically annotated with a note telling
the LLM not to call the tool again if it has already returned a
pending/intermediate status (matching Python ADK's behavior).

# `t`

```elixir
@type t() :: %ADK.Tool.LongRunningTool{
  description: String.t(),
  func: tool_func(),
  name: String.t(),
  parameters: map(),
  timeout: pos_integer()
}
```

# `tool_func`

```elixir
@type tool_func() :: (ADK.ToolContext.t(), map(), update_fn() -&gt; term())
```

# `update_fn`

```elixir
@type update_fn() :: (String.t() -&gt; :ok)
```

# `new`

```elixir
@spec new(
  atom() | String.t(),
  keyword()
) :: t()
```

Create a new long-running tool.

## Options

- `:description` — Human-readable description (automatically annotated with long-running notice)
- `:func` — The tool function `(tool_ctx, args, send_update) -> result`
- `:parameters` — JSON Schema map for parameters
- `:timeout` — Milliseconds to wait before timing out (default: 60_000)

## Examples

    iex> tool = ADK.Tool.LongRunningTool.new(:slow_tool,
    ...>   description: "Does slow work",
    ...>   func: fn _ctx, _args, _send_update -> "done" end,
    ...>   parameters: %{type: "object", properties: %{}}
    ...> )
    iex> tool.name
    "slow_tool"
    iex> String.contains?(tool.description, "long-running operation")
    true

# `run`

```elixir
@spec run(t(), ADK.ToolContext.t() | nil, map()) :: ADK.Tool.result()
```

Execute the long-running tool asynchronously.

Spawns a supervised Task under `ADK.RunnerSupervisor`, collects
any status updates the function sends, and awaits the final result
within the configured timeout.

Status updates from `send_update.(msg)` are collected in order.
If any updates were sent, the result is wrapped:
`{:ok, %{result: final_value, status_updates: ["update 1", ...]}}`.
If no updates were sent, returns `{:ok, final_value}` directly.

## Examples

    iex> tool = ADK.Tool.LongRunningTool.new(:fast_tool,
    ...>   func: fn _ctx, %{"x" => x}, _send_update -> x * 2 end,
    ...>   parameters: %{},
    ...>   timeout: 5_000
    ...> )
    iex> {:ok, result} = ADK.Tool.LongRunningTool.run(tool, nil, %{"x" => 21})
    iex> result
    42

---

*Consult [api-reference.md](api-reference.md) for complete listing*
