symfonic.agent.backend.messages¶
messages ¶
Conversion between the facade's typed Message and LangChain messages.
One direction builds the request (RES-2's "system, history, user" ordering); the other records what came back. Both are pure functions so the invocation loop stays readable and this file is the only place the wire shape is known.
assistant_message ¶
The facade-typed record of one assistant turn.
The same ToolCall objects appear here and on AgentResult.tool_calls,
so a caller may walk either view and compare by identity (RES-3).
Source code in src/symfonic/agent/backend/messages.py
build_request ¶
build_request(prompt: str, *, instructions: str | None, system_blocks: Sequence[Any] = (), history: Sequence[Message], attachments: Sequence[Attachment], family: ProviderFamily) -> tuple[list[Message], list[BaseMessage]]
Return the turn's facade messages and their LangChain equivalents.
Order is RES-2's: the system message when instructions is set, the
replayed history, then the user message. instructions is used verbatim
— None sends no system message rather than substituting a framework
default (FAC-5).
When instructions is set, system messages already in history are
dropped rather than replayed. RES-2 makes result.messages a lossless
round trip, and those messages carry the previous turn's system message
— replaying it alongside the freshly prepended one sends the instructions
twice on turn two, three times on turn three, since providers concatenate
system blocks instead of deduplicating them. instructions is the one
source of the system prompt for this agent, so the replayed copies go.
When instructions is None the history is replayed untouched: a
transcript handed to an agent that declares no instructions of its own
keeps the ones it was recorded with.
Source code in src/symfonic/agent/backend/messages.py
normalize_stop_reason ¶
Map the provider's terminal reason onto the facade's four, or None.
Source code in src/symfonic/agent/backend/messages.py
text_of ¶
Flatten LangChain message content to text; never None (RES-1).
Content is a str for text-only replies and a list of typed blocks when
the provider interleaves reasoning or tool use; only text blocks are
answer text.
Source code in src/symfonic/agent/backend/messages.py
to_langchain ¶
Convert one facade message into its LangChain equivalent.
content is the str Message declares, except for a message
replayed out of caller history that carried a LangChain block list —
an image, a document, a provider reasoning block. Message.content has
no room for those, so the converter that built the message kept them on
the side (blocks) and this is where they go back on the wire. Dropping
them here would hand the provider a conversation with the picture removed
while the legacy route, which replays the caller's BaseMessage
verbatim, hands it the picture.