Nous.Agent.Context (nous v0.9.0)

View Source

Unified context for agent execution.

Accumulates state across the agent loop:

  • Conversation messages
  • Tool call history
  • Usage tracking
  • User dependencies
  • Callbacks configuration

Example

# Create new context
ctx = Context.new(
  system_prompt: "You are helpful",
  deps: %{database: MyDB},
  max_iterations: 15
)

# Add messages
ctx = ctx
|> Context.add_message(Message.user("Hello"))
|> Context.add_message(Message.assistant("Hi there!"))

# Check loop control
if ctx.needs_response do
  # Continue execution
end

Callbacks

Callbacks can be configured as a map of event handlers:

ctx = Context.new(callbacks: %{
  on_llm_new_delta: fn _event, delta -> IO.write(delta) end,
  on_tool_call: fn _event, call -> IO.inspect(call) end
})

Process Notification

For LiveView integration, set notify_pid:

ctx = Context.new(notify_pid: self())
# Will receive: {:agent_delta, text}, {:tool_call, call}, etc.

Summary

Functions

Add a message to the context.

Add multiple messages to the context.

Record a tool call in the context.

Merge usage statistics into the context.

Get all assistant messages from the context.

Create context from an existing RunContext (migration helper).

Increment the iteration counter.

Get the last message from the context.

Check if maximum iterations has been reached.

Merge new dependencies into the context.

Create a new context with options.

Set needs_response flag explicitly.

Convert to RunContext for tool execution (backwards compatibility).

Types

callback_fn()

@type callback_fn() :: (atom(), any() -> any())

t()

@type t() :: %Nous.Agent.Context{
  agent_name: String.t() | nil,
  callbacks: %{optional(atom()) => callback_fn()},
  cancellation_check: (-> :ok | {:error, term()}) | nil,
  deps: map(),
  iteration: non_neg_integer(),
  max_iterations: non_neg_integer(),
  messages: [Nous.Message.t()],
  needs_response: boolean(),
  notify_pid: pid() | nil,
  started_at: DateTime.t() | nil,
  system_prompt: String.t() | nil,
  tool_calls: [map()],
  usage: Nous.Usage.t()
}

Functions

add_message(ctx, message)

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

Add a message to the context.

Automatically updates needs_response based on message role and content.

Examples

iex> ctx = Context.new()
iex> ctx = Context.add_message(ctx, Message.user("Hello"))
iex> length(ctx.messages)
1

add_messages(ctx, messages)

@spec add_messages(t(), [Nous.Message.t()]) :: t()

Add multiple messages to the context.

Examples

iex> ctx = Context.new()
iex> messages = [Message.user("Hi"), Message.assistant("Hello")]
iex> ctx = Context.add_messages(ctx, messages)
iex> length(ctx.messages)
2

add_tool_call(ctx, call)

@spec add_tool_call(t(), map()) :: t()

Record a tool call in the context.

Examples

iex> ctx = Context.new()
iex> call = %{id: "call_123", name: "search", arguments: %{"q" => "test"}}
iex> ctx = Context.add_tool_call(ctx, call)
iex> length(ctx.tool_calls)
1

add_usage(ctx, usage)

@spec add_usage(t(), Nous.Usage.t() | map()) :: t()

Merge usage statistics into the context.

Examples

iex> ctx = Context.new()
iex> usage = %Usage{input_tokens: 100, output_tokens: 50}
iex> ctx = Context.add_usage(ctx, usage)
iex> ctx.usage.input_tokens
100

assistant_messages(context)

@spec assistant_messages(t()) :: [Nous.Message.t()]

Get all assistant messages from the context.

Examples

iex> ctx = Context.new()
iex> ctx = ctx |> Context.add_message(Message.user("Hi"))
iex> ctx = ctx |> Context.add_message(Message.assistant("Hello"))
iex> length(Context.assistant_messages(ctx))
1

from_run_context(run_ctx, opts \\ [])

@spec from_run_context(
  Nous.RunContext.t(),
  keyword()
) :: t()

Create context from an existing RunContext (migration helper).

Examples

iex> run_ctx = Nous.RunContext.new(%{key: "value"})
iex> ctx = Context.from_run_context(run_ctx)
iex> ctx.deps.key
"value"

increment_iteration(ctx)

@spec increment_iteration(t()) :: t()

Increment the iteration counter.

Examples

iex> ctx = Context.new()
iex> ctx = Context.increment_iteration(ctx)
iex> ctx.iteration
1

last_message(context)

@spec last_message(t()) :: Nous.Message.t() | nil

Get the last message from the context.

Examples

iex> ctx = Context.new() |> Context.add_message(Message.user("Hello"))
iex> Context.last_message(ctx).content
"Hello"

iex> ctx = Context.new()
iex> Context.last_message(ctx)
nil

max_iterations_reached?(context)

@spec max_iterations_reached?(t()) :: boolean()

Check if maximum iterations has been reached.

Examples

iex> ctx = Context.new(max_iterations: 5, iteration: 5)
iex> Context.max_iterations_reached?(ctx)
true

iex> ctx = Context.new(max_iterations: 5, iteration: 3)
iex> Context.max_iterations_reached?(ctx)
false

merge_deps(ctx, new_deps)

@spec merge_deps(t(), map()) :: t()

Merge new dependencies into the context.

Used by tools to update context state via __update_context__ or ContextUpdate.

Examples

iex> ctx = Context.new(deps: %{count: 0})
iex> ctx = Context.merge_deps(ctx, %{count: 1, new_key: "value"})
iex> ctx.deps.count
1
iex> ctx.deps.new_key
"value"

new(opts \\ [])

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

Create a new context with options.

Options

  • :messages - Initial message list (default: [])
  • :system_prompt - System prompt string
  • :deps - User dependencies map (default: %{})
  • :max_iterations - Maximum loop iterations (default: 10)
  • :callbacks - Map of callback functions
  • :notify_pid - PID to receive event messages
  • :agent_name - Name for telemetry/logging
  • :cancellation_check - Function to check for cancellation

Examples

iex> ctx = Context.new(system_prompt: "Be helpful", max_iterations: 5)
iex> ctx.max_iterations
5

iex> ctx = Context.new(deps: %{user_id: 123})
iex> ctx.deps.user_id
123

set_needs_response(ctx, value)

@spec set_needs_response(t(), boolean()) :: t()

Set needs_response flag explicitly.

Examples

iex> ctx = Context.new()
iex> ctx = Context.set_needs_response(ctx, false)
iex> ctx.needs_response
false

to_run_context(ctx)

@spec to_run_context(t()) :: Nous.RunContext.t()

Convert to RunContext for tool execution (backwards compatibility).

This allows tools to continue using the existing RunContext interface.

Examples

iex> ctx = Context.new(deps: %{db: :postgres})
iex> run_ctx = Context.to_run_context(ctx)
iex> run_ctx.deps.db
:postgres