symfonic.capabilities.memory.families¶
families ¶
Reading one model reply, whatever provider family produced it.
The legacy extractor normalises a reply with
response.content if hasattr(response, "content") else str(response)
(symfonic.memory._internal.llm.response_parser). That is right for exactly
one family — a LangChain message whose content is a string — and wrong in a
way that is hard to see for the rest:
- An Anthropic reply is a list of blocks.
str(list)renders Python reprs, so the JSON scraper downstream mines the transport rather than the answer, and athinkingblock's speculation is indistinguishable from the model's conclusion. - An OpenAI or Gemini envelope has no
contentattribute at all, so the fallback stringifies the whole envelope — ids, usage counters, finish reasons — and the first{the scraper finds belongs to none of them.
So this module reads each family's own shape, and refuses the shapes it does not recognise rather than stringifying them. An unreadable reply produces no text and says so; a reply mined out of an envelope produces memories nobody said, which is worse than producing none.
Block kinds that are not the answer (thinking, tool_use, images) are
skipped and named in :attr:ProviderReply.ignored, because "the model
answered nothing" and "the model answered in a block we dropped" are different
diagnoses.
ProviderFamily ¶
Bases: StrEnum
The reply shapes this capability knows how to read.
ProviderReply
dataclass
¶
One model reply, read down to the text the model actually emitted.
json_payload ¶
The first JSON object in text, or None.
Same brace-matching heuristic the legacy parser uses, with one addition:
a payload that parses to something other than an object is refused rather
than returned. json.loads on [1, 2] succeeds, and a caller that
then asks it for ops gets an AttributeError from inside a
post-response stage instead of "the model did not answer in our format".
Source code in src/symfonic/capabilities/memory/families.py
read_reply ¶
Read response into text, or report that its shape is unknown.