# `Puck.Backend`
[🔗](https://github.com/bradleygolden/puck/blob/v0.2.25/lib/puck/backend.ex#L1)

Behaviour for LLM backend implementations.

## Callbacks

- `call/3` - Synchronous call to the LLM
- `stream/3` - Streaming call returning an enumerable
- `introspect/1` - (optional) Returns backend metadata

## Example

    defmodule MyBackend do
      @behaviour Puck.Backend

      @impl true
      def call(config, messages, opts) do
        {:ok, Puck.Response.new(content: "Hello!", finish_reason: :stop)}
      end

      @impl true
      def stream(config, messages, opts) do
        stream = Stream.map(["Hello", " ", "world!"], &%{content: &1})
        {:ok, stream}
      end
    end

# `chunk`

```elixir
@type chunk() :: %{content: term(), metadata: map()}
```

# `config`

```elixir
@type config() :: map()
```

# `error`

```elixir
@type error() :: {:error, term()}
```

# `introspection`

```elixir
@type introspection() :: %{
  :provider =&gt; String.t(),
  :model =&gt; String.t(),
  :operation =&gt; operation(),
  optional(atom()) =&gt; term()
}
```

# `messages`

```elixir
@type messages() :: [Puck.Message.t()]
```

# `operation`

```elixir
@type operation() :: :chat | :embeddings | :completion | atom()
```

Metadata returned by introspect/1 for observability.

These align with OpenTelemetry GenAI semantic conventions.

# `opts`

```elixir
@type opts() :: keyword()
```

# `call`

```elixir
@callback call(config(), messages(), opts()) :: {:ok, Puck.Response.t()} | error()
```

Makes a synchronous call to the LLM.

## Parameters

- `config` - Backend configuration from the agent tuple (model, API keys, etc.)
- `messages` - List of `Puck.Message` structs
- `opts` - Additional options:
  - `:output_schema` - Zoi schema for structured output parsing (Puck opt)
  - `:backend_opts` - Options passed through to the underlying library

## Returns

- `{:ok, response}` with a `Puck.Response` struct
- `{:error, reason}` on failure

# `introspect`
*optional* 

```elixir
@callback introspect(config()) :: introspection()
```

Returns metadata about a backend configuration.

Used by telemetry/tracing to identify provider and model.
Aligns with OpenTelemetry GenAI conventions.

## Parameters

- `config` - The backend configuration map (same as passed to call/3)

## Expected Keys

- `:provider` - Provider name ("anthropic", "openai", "google", etc.)
- `:model` - Configured model identifier
- `:operation` - Operation type (:chat, :embeddings, etc.)

## Example

    @impl true
    def introspect(config) do
      %{
        provider: "anthropic",
        model: Map.get(config, :model, "unknown"),
        operation: :chat
      }
    end

Note: For the actual model used in a specific call (which may differ
from the configured model), check `response.metadata.model`.

# `stream`

```elixir
@callback stream(config(), messages(), opts()) :: {:ok, Enumerable.t()} | error()
```

Makes a streaming call to the LLM.

## Parameters

- `config` - Backend configuration from the agent tuple
- `messages` - List of `Puck.Message` structs
- `opts` - Additional options:
  - `:backend_opts` - Options passed through to the underlying library

## Returns

- `{:ok, stream}` where stream is an enumerable of chunks
- `{:error, reason}` on failure

Note: Streaming typically returns text chunks. For structured output
with streaming, the full response must be accumulated and parsed.

---

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