Current section
Files
Jump to
Current section
Files
priv/templates/agent_persistence.ex.eex
defmodule <%= module %> do
@moduledoc """
Implements `Sagents.AgentPersistence` for state snapshots.
Persists full agent state (messages, todos, metadata) to the database
via `<%= conversations_module %>.save_agent_state/3`, and mirrors the
durable interrupt flag onto `conversation.metadata["interrupted"]` via
`set_interrupted/3` (called by sagents only on actual transitions).
"""
@behaviour Sagents.AgentPersistence
require Logger
@impl true
def persist_state(scope, state_data, context) do
conversation_id = extract_conversation_id(context.agent_id)
case <%= conversations_module %>.save_agent_state(scope, conversation_id, state_data) do
{:ok, _agent_state} ->
Logger.debug("Persisted agent state for #{context.agent_id} (#{context.lifecycle})")
:ok
{:error, %Ecto.Changeset{errors: errors}} = error ->
if conversation_deleted?(errors) do
Logger.warning(
"Skipping agent state persistence for #{context.agent_id} (#{context.lifecycle}): conversation no longer exists"
)
:ok
else
error
end
{:error, :not_found} ->
Logger.warning(
"Skipping agent state persistence for #{context.agent_id} (#{context.lifecycle}): conversation not accessible in scope"
)
:ok
end
end
@impl true
def load_state(scope, context) do
conversation_id = extract_conversation_id(context.agent_id)
<%= conversations_module %>.load_agent_state(scope, conversation_id)
end
@impl true
def set_interrupted(scope, context, interrupted?) do
conversation_id = extract_conversation_id(context.agent_id)
case <%= conversations_module %>.set_interrupt_status(scope, conversation_id, interrupted?) do
{:ok, _conversation} ->
:ok
{:error, :not_found} ->
Logger.warning(
"Skipping interrupt flag update for #{context.agent_id}: conversation not accessible in scope"
)
:ok
other ->
Logger.warning(
"Failed to update interrupt flag for #{context.agent_id}: #{inspect(other)}"
)
:ok
end
end
defp extract_conversation_id(agent_id) do
String.replace_prefix(agent_id, "conversation-", "")
end
defp conversation_deleted?(changeset_errors) do
Enum.any?(changeset_errors, fn
{:conversation_id, {_msg, opts}} -> Keyword.get(opts, :constraint) == :foreign
_other -> false
end)
end
end