# `Omni.Tools.WebFetch.Strategy`
[🔗](https://github.com/aaronrussell/omni_tools/blob/v0.1.0/lib/omni/tools/web_fetch/strategy.ex#L1)

Behaviour for URL-specific content extraction strategies.

A strategy controls how a URL is fetched and how the response is
converted to text for LLM consumption. Strategies are matched against
URLs in order — the first strategy whose `c:match?/2` returns `true`
handles the request.

## Callbacks

  * `c:match?/2` (required) — returns `true` if this strategy handles
    the given URI.
  * `c:request/2` (optional) — modifies the `Req.Request` before
    execution (URL rewriting, custom headers, etc.).
  * `c:extract/2` (required) — converts the `Req.Response` into a
    content string.

## Example

    defmodule MyApp.WikiStrategy do
      @behaviour Omni.Tools.WebFetch.Strategy

      @impl true
      def match?(uri, _opts), do: uri.host == "en.wikipedia.org"

      @impl true
      def request(req, _opts) do
        # Use the mobile API for cleaner content
        Req.merge(req, headers: [{"accept", "text/html"}])
      end

      @impl true
      def extract(response, _opts) do
        Html2Markdown.convert(response.body)
      end
    end

## Usage

Pass strategies to `Omni.Tools.WebFetch.new/1`:

    Omni.Tools.WebFetch.new(
      strategies: [
        {MyApp.WikiStrategy, []},
        MyApp.AnotherStrategy
      ]
    )

# `extract`

```elixir
@callback extract(Req.Response.t(), opts :: keyword()) :: String.t()
```

Extracts content from the response as a string.

# `match?`

```elixir
@callback match?(URI.t(), opts :: keyword()) :: boolean()
```

Returns `true` if this strategy handles the given URI.

# `request`
*optional* 

```elixir
@callback request(Req.Request.t(), opts :: keyword()) :: Req.Request.t()
```

Modifies the `Req.Request` before execution.

# `find`

```elixir
@spec find([{module(), keyword()}], URI.t()) :: {module(), keyword()} | nil
```

Finds the first strategy matching the given URI.

Returns `{module, opts}` for the first strategy whose `match?/2`
returns `true`, or `nil` if none match.

# `resolve`

```elixir
@spec resolve([module() | {module(), keyword()}]) :: [{module(), keyword()}]
```

Normalizes a list of strategy specs into `{module, opts}` tuples.

Accepts bare modules or `{module, opts}` tuples. Validates that each
module implements the required callbacks. Raises `ArgumentError` on
invalid input.

    Strategy.resolve([MyStrategy, {OtherStrategy, key: "val"}])
    #=> [{MyStrategy, []}, {OtherStrategy, [key: "val"]}]

---

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