Packages
nous
0.16.4
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/session/guardrails.ex
defmodule Nous.Session.Guardrails do
@moduledoc """
Session limit checking and guardrail functions.
Works with `Nous.Session.Config` to enforce turn limits,
token budgets, and compaction triggers.
## Example: GenServer integration
defmodule MySession do
use GenServer
def handle_call({:send, message}, _from, state) do
case Guardrails.check_limits(state.config, state.turns, state.in_tokens, state.out_tokens) do
:ok ->
# proceed with agent call
...
{:error, reason} ->
{:reply, {:error, reason}, state}
end
end
end
"""
alias Nous.Session.Config
@doc """
Checks whether a session has exceeded its configured limits.
Returns `:ok` if within limits, or `{:error, reason}` if a limit is hit.
## Examples
iex> config = %Nous.Session.Config{max_turns: 10, max_budget_tokens: 100_000}
iex> Nous.Session.Guardrails.check_limits(config, 5, 1000, 2000)
:ok
iex> config = %Nous.Session.Config{max_turns: 10, max_budget_tokens: 100_000}
iex> Nous.Session.Guardrails.check_limits(config, 10, 1000, 2000)
{:error, :max_turns_reached}
"""
@spec check_limits(Config.t(), non_neg_integer(), non_neg_integer(), non_neg_integer()) ::
:ok | {:error, :max_turns_reached | :max_budget_reached}
def check_limits(%Config{} = config, turn_count, input_tokens, output_tokens) do
cond do
turn_count >= config.max_turns ->
{:error, :max_turns_reached}
input_tokens + output_tokens >= config.max_budget_tokens ->
{:error, :max_budget_reached}
true ->
:ok
end
end
@doc """
Checks if the session should trigger compaction.
## Examples
iex> config = %Nous.Session.Config{compact_after_turns: 20}
iex> Nous.Session.Guardrails.should_compact?(config, 25)
true
iex> config = %Nous.Session.Config{compact_after_turns: 20}
iex> Nous.Session.Guardrails.should_compact?(config, 15)
false
"""
@spec should_compact?(Config.t(), non_neg_integer()) :: boolean()
def should_compact?(%Config{compact_after_turns: threshold}, turn_count) do
turn_count > threshold
end
@doc """
Returns remaining budget as `{remaining_turns, remaining_tokens}`.
## Examples
iex> config = %Nous.Session.Config{max_turns: 10, max_budget_tokens: 100_000}
iex> Nous.Session.Guardrails.remaining(config, 3, 5000, 10000)
{7, 85000}
"""
@spec remaining(Config.t(), non_neg_integer(), non_neg_integer(), non_neg_integer()) ::
{non_neg_integer(), non_neg_integer()}
def remaining(%Config{} = config, turn_count, input_tokens, output_tokens) do
remaining_turns = max(0, config.max_turns - turn_count)
remaining_tokens = max(0, config.max_budget_tokens - (input_tokens + output_tokens))
{remaining_turns, remaining_tokens}
end
@doc """
Returns a summary map of the session's current state against its limits.
Useful for logging or exposing session health to users.
## Examples
Nous.Session.Guardrails.summary(config, 5, 10_000, 20_000)
#=> %{
#=> turns: %{current: 5, max: 10, remaining: 5},
#=> tokens: %{used: 30_000, max: 200_000, remaining: 170_000},
#=> needs_compaction: false
#=> }
"""
@spec summary(Config.t(), non_neg_integer(), non_neg_integer(), non_neg_integer()) :: map()
def summary(%Config{} = config, turn_count, input_tokens, output_tokens) do
used_tokens = input_tokens + output_tokens
{rem_turns, rem_tokens} = remaining(config, turn_count, input_tokens, output_tokens)
%{
turns: %{current: turn_count, max: config.max_turns, remaining: rem_turns},
tokens: %{used: used_tokens, max: config.max_budget_tokens, remaining: rem_tokens},
needs_compaction: should_compact?(config, turn_count)
}
end
end