Sagents.MessagePreprocessor behaviour (Sagents v0.4.2)
Copy MarkdownBehaviour for transforming user-submitted messages into separate display and LLM representations.
When a user sends a message, the system may need to produce two different versions:
Display message — The user-facing representation persisted for UI rendering. For example,
@ProjectBriefmight render as a styled link/chip.LLM message — The message added to the model's conversation history. For example,
@ProjectBriefmight be expanded to include the full document content so the model can reason over it.
When this runs
Called inside AgentServer.handle_call({:add_message, ...}) before the message
is added to state or persisted as a display message. Only called for messages submitted
via AgentServer.add_message/2 — NOT for messages generated during LLM execution
(assistant responses, tool results).
Content representation
Both the display message and the LLM message are regular LangChain.Message structs.
The behaviour only constrains the return shape — not what's inside the messages.
Implementations are free to use whatever content representation makes sense:
- Plain text with HTML snippets for display rendering
- XML-tagged metadata for structured LLM context
- Structured content maps for richer UI rendering
Configuration
Per-agent via AgentServer/AgentSupervisor start options:
AgentSupervisor.start_link(
agent: agent,
conversation_id: conversation_id,
message_preprocessor: MyApp.MentionPreprocessor,
# ...
)If not configured, messages flow through unchanged to both paths.
Summary
Callbacks
Transform a user-submitted message into separate display and LLM representations.
Callbacks
@callback preprocess(message :: LangChain.Message.t(), context :: map()) :: {:ok, display_message :: LangChain.Message.t(), llm_message :: LangChain.Message.t()} | {:error, term()}
Transform a user-submitted message into separate display and LLM representations.
Context
The context map provides:
:agent_id— The agent identifier string:conversation_id— The conversation identifier (may be nil):tool_context— The caller-supplied context map from the Agent struct. Contains:current_scope(the Phoenix Scope with the current user), injected by the Coordinator at session start. Use this to scope document lookups, permission checks, etc.:state— The currentSagents.State(messages, metadata). Useful for context-aware preprocessing (e.g., referencing prior messages).
Returns
{:ok, display_message, llm_message}— Use different messages for each path. For passthrough, return the same message for both.{:error, reason}— Reject the message. AgentServer replies with{:error, reason}.