# `ALLM.Thread`
[🔗](https://github.com/cykod/ALLM/blob/v0.5.0/lib/allm/thread.ex#L1)

An ordered message log — Layer A serializable data.

A thread is the canonical container for the message history threaded
through a session or chat loop. It is append-only at the API surface
(helpers build new threads rather than mutating an existing one).

## Fields

| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `:messages` | `[%ALLM.Message{}]` | `[]` | Ordered conversation. |
| `:metadata` | `map` | `%{}` | Caller-owned. |

## Round-trip

    iex> t = ALLM.Thread.new() |> ALLM.Thread.add_user("hi")
    iex> json = ALLM.Serializer.to_json!(t)
    iex> {:ok, ^t} = ALLM.Serializer.from_json(json)
    iex> length(t.messages)
    1

See also `guides/sessions.md`.

# `t`

```elixir
@type t() :: %ALLM.Thread{messages: [ALLM.Message.t()], metadata: map()}
```

# `add_assistant`

```elixir
@spec add_assistant(t(), String.t()) :: t()
```

Append an assistant-role message with the given text content.

## Examples

    iex> ALLM.Thread.new() |> ALLM.Thread.add_assistant("hello") |> ALLM.Thread.last_message()
    %ALLM.Message{role: :assistant, content: "hello", name: nil, tool_call_id: nil, metadata: %{}}

# `add_message`

```elixir
@spec add_message(t(), ALLM.Message.t()) :: t()
```

Append a single message to the end of the thread.

## Examples

    iex> t = ALLM.Thread.new() |> ALLM.Thread.add_message(%ALLM.Message{role: :user, content: "hi"})
    iex> length(t.messages)
    1

# `add_messages`

```elixir
@spec add_messages(t(), [ALLM.Message.t()]) :: t()
```

Append multiple messages to the end of the thread, preserving order.

## Examples

    iex> t = ALLM.Thread.new() |> ALLM.Thread.add_messages([
    ...> %ALLM.Message{role: :user, content: "a"},
    ...> %ALLM.Message{role: :assistant, content: "b"}
    ...> ])
    iex> Enum.map(t.messages, & &1.content)
    ["a", "b"]

# `add_system`

```elixir
@spec add_system(t(), String.t()) :: t()
```

Append a system-role message with the given text content.

## Examples

    iex> ALLM.Thread.new() |> ALLM.Thread.add_system("be nice") |> ALLM.Thread.last_message()
    %ALLM.Message{role: :system, content: "be nice", name: nil, tool_call_id: nil, metadata: %{}}

# `add_user`

```elixir
@spec add_user(t(), String.t()) :: t()
```

Append a user-role message with the given text content.

## Examples

    iex> ALLM.Thread.new() |> ALLM.Thread.add_user("hi") |> ALLM.Thread.last_message()
    %ALLM.Message{role: :user, content: "hi", name: nil, tool_call_id: nil, metadata: %{}}

# `from_messages`

```elixir
@spec from_messages([ALLM.Message.t()]) :: t()
```

Build a thread from an existing list of messages.

## Examples

    iex> t = ALLM.Thread.from_messages([%ALLM.Message{role: :user, content: "hi"}])
    iex> length(t.messages)
    1

# `last_message`

```elixir
@spec last_message(t()) :: ALLM.Message.t() | nil
```

Return the last message in the thread, or `nil` when the thread is empty.

## Examples

    iex> ALLM.Thread.last_message(ALLM.Thread.new())
    nil

    iex> ALLM.Thread.new()
    ...> |> ALLM.Thread.add_user("first")
    ...> |> ALLM.Thread.add_user("last")
    ...> |> ALLM.Thread.last_message()
    ...> |> Map.get(:content)
    "last"

# `messages`

```elixir
@spec messages(t()) :: [ALLM.Message.t()]
```

Return the ordered list of messages in the thread.

## Examples

    iex> ALLM.Thread.messages(ALLM.Thread.new())
    []

# `new`

```elixir
@spec new(keyword()) :: t()
```

Build a `%Thread{}` from keyword opts.

## Examples

    iex> ALLM.Thread.new()
    %ALLM.Thread{messages: [], metadata: %{}}

    iex> ALLM.Thread.new(metadata: %{trace: "x"}).metadata
    %{trace: "x"}

---

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