ExAgent.Context (ExAgent v0.4.1)

Copy Markdown View Source

Portable conversation state shared across agents and patterns.

Holds the message history, metadata, and an optional parent reference for linking back to an orchestrator in the subagent pattern.

Summary

Functions

Appends a message to the context.

Returns the last assistant message from the context, or nil if none exists.

Creates a new context with optional initial values.

Drops the oldest messages, keeping at most max of them.

Types

t()

@type t() :: %ExAgent.Context{
  messages: [ExAgent.Message.t()],
  metadata: map(),
  parent_ref: reference() | nil
}

Functions

add_message(context, message)

@spec add_message(t(), ExAgent.Message.t()) :: t()

Appends a message to the context.

Examples

iex> {:ok, msg} = ExAgent.Message.new(role: :user, content: "Hello")
iex> ctx = ExAgent.Context.new() |> ExAgent.Context.add_message(msg)
iex> length(ctx.messages)
1

get_last_assistant_message(context)

@spec get_last_assistant_message(t()) :: ExAgent.Message.t() | nil

Returns the last assistant message from the context, or nil if none exists.

Examples

iex> ExAgent.Context.get_last_assistant_message(ExAgent.Context.new())
nil

new(opts \\ [])

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

Creates a new context with optional initial values.

Examples

iex> ctx = ExAgent.Context.new()
iex> ctx.messages
[]

iex> ctx = ExAgent.Context.new(metadata: %{session: "abc"})
iex> ctx.metadata
%{session: "abc"}

trim(context, max)

@spec trim(t(), pos_integer()) :: t()

Drops the oldest messages, keeping at most max of them.

Conversation history otherwise grows without bound: every turn resends the whole transcript, so cost climbs turn over turn until the model returns :context_length and the agent is stuck. Trimming is opt-in - silently forgetting what a user said is a decision the caller has to make.

Leading :system messages are always kept: they carry instructions that must outlive the window. A :tool result is never left without the :assistant message that requested it, since providers reject an orphaned result.

Examples

iex> {:ok, a} = ExAgent.Message.new(role: :user, content: "one")
iex> {:ok, b} = ExAgent.Message.new(role: :user, content: "two")
iex> ctx = ExAgent.Context.new(messages: [a, b])
iex> ExAgent.Context.trim(ctx, 1).messages |> Enum.map(& &1.content)
["two"]