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

An ordered message log. See spec §5.6 and §14.

Layer A — pure 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).

# `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*
