Skip to contents

Renders a shared, speaker-attributed transcript into a message array from one speaker's point of view, the way a stateful chat is post-trained to read it: the current speaker's own past turns become assistant messages (their own voice), and every other speaker's turns become user messages labeled with the speaker's name. An optional system block (persona, instructions) leads the array, and an optional instruction (the current question or "your turn" cue) closes it as a trailing user message. The result is passed through ensure_alternating_messages() so it is provider-safe.

Usage

transcript_as_messages(
  transcript,
  speaker,
  system = NULL,
  instruction = NULL,
  label = function(id) paste0(id, ": "),
  sanitize = TRUE,
  ensure_final_user = TRUE
)

Arguments

transcript

A data.frame/tibble with character columns speaker and text, in chronological order.

speaker

The current speaker's id (matched against transcript$speaker). Rows with this speaker become assistant turns; all others become labeled user turns.

system

Optional system text (persona, standing instructions) placed as the leading system message. NULL to omit.

instruction

Optional trailing instruction (current question, "your turn" cue) placed as the final user message. NULL to omit.

label

A function (speaker_id) -> prefix producing the in-content label for other speakers' turns; the default renders "<id>: ". The speaker's own turns are never labeled.

sanitize

If TRUE (default), strip forged role headers and chat control tokens from other speakers' text before embedding.

ensure_final_user

If TRUE (default) and no instruction is supplied and the last turn is the speaker's own (an assistant turn), append a minimal user cue so the array ends on a user turn (a sound next-turn generation boundary). Set FALSE for verbatim archival conversion. Missing text is coerced to ""; a missing speaker is an error.

Value

A list of message objects (list(role=, content=)) suitable for call_llm(), guaranteed alternating and user-leading after any system.

Details

Putting the speaker's own turns in the assistant role gives the model a structural handle on "what I already said", which reduces self-repetition in multi-agent conversations relative to flattening the whole transcript into one user block. Other speakers' text stays in user turns (never assistant), which preserves the trust boundary and is sanitized against forged role headers.

Examples

tx <- data.frame(
  speaker = c("Ana", "Ben", "Cy"),
  text    = c("I think we ban it.", "Too costly for renters.", "Phase it in."),
  stringsAsFactors = FALSE
)
# From Ben's perspective: his line is assistant, Ana's and Cy's are user.
transcript_as_messages(tx, speaker = "Ben",
                       system = "You are Ben, a renter.",
                       instruction = "Your turn, Ben.")