Post-prompt normalization
Post-prompt normalization
One conversation can run over voice and over text chat, and both produce a post-prompt body, but not
the same shape. The signalwire.core.post_prompt module absorbs that divergence so your
application sees one artifact regardless of which engine finished the conversation.
conversation_type is a reliable top-level discriminator on both. The voice engine can also
deliver post_prompt_data as {"parsed": [ {...} ], "raw": "..."}, an object wrapped in a
list, which passes structural checks and misses every field lookup. The parser unwraps it.
The module doesn’t decide what a summary should contain. The schema is whatever your post-prompt text asked the model to produce, so parsing is schema-agnostic and returns the dict as found. Nothing here raises: the conversation that produced the body is already over.
Properties
NormalizedPostPrompt is a frozen dataclass, one finished conversation leg in a shape that doesn’t
vary by engine.
medium
conversation_type as reported, such as "voice" or "chat". Empty when the engine didn’t say.
conversation_id
Present on chat, absent on voice. When None, fall back to your own key from global_data or
call_id rather than treating this as authoritative.
summary
The parsed post_prompt_data, with whatever keys your post-prompt asked for. {} when there was
none or it couldn’t be parsed. A model that answered in prose instead of JSON yields
{"summary": "<the prose>"}.
dialogue
user and assistant turns only, as {"role", "content"} pairs, with tool calls and the chat
engine’s summary echo removed.
call_id
The platform call ID, when present.
raw
The complete request body, untouched.
Functions
Normalize a post-prompt body from either engine.
Return post_prompt_data as a plain dict, whichever shape it arrived in.
Extract the user and assistant turns from a call log.
Unwrap a fenced JSON code block.
Example
Store every finished leg the same way, whether it came from
on_summary() or on_call_end():