Conversation history is an application policy
The API does not inspect Room automatically. PocketChat explicitly constructs request parts from locally stored completed turns. This makes context inspectable and lets the application decide which text is eligible. More history is not always better: old constraints may be irrelevant, and every included character increases request size.
The baseline function is small enough to reason about completely:
fun buildContext(turns: List<Turn>, prompt: String, maxChars: Int = 24000): ContextWindow {
require(prompt.length <= 6000 && prompt.length <= maxChars) { "Keep your question under 6,000 characters." }
var remaining = maxChars - prompt.length
val selected = mutableListOf<Turn>()
val eligible = turns.filter { it.status == ReplyStatus.COMPLETE }
for (turn in eligible.asReversed()) {
val size = turn.prompt.length + turn.answer.length
if (size > remaining) break
selected.add(0, turn); remaining -= size
}
return ContextWindow(selected.flatMap { listOf(ChatPart("user", it.prompt), ChatPart("model", it.answer)) } + ChatPart("user", prompt), selected.size < eligible.size)
}
It starts with the newest completed pair, subtracts whole-pair sizes, and stops when the next pair does not fit. It never sends an answer without its question. It preserves chronology by inserting accepted turns at the beginning, then appends the newest user question.
The 6,000-character prompt limit and 24,000-character context budget are conservative product limits, not exact token measurements. Tokenization varies across languages and content. The character budget also does not include the separate system instruction. A production budget should account for instructions, tools, model limits, output allowance, and a tokenizer or documented counting API.
Your tested extension
Add a positive maxCompletedPairs parameter with a default of six. A learner can now bound both recent-pair count and character size. Preserve the omitted flag whenever either budget excludes an eligible turn. Failed and stopped turns do not consume this completed-pair allowance.
The codelab supplies tests and the complete replacement function. First introduce the parameter without implementing its behavior, observe the failing assertions, then implement the count guard. This prevents confusing a test that merely compiles with evidence that the policy works.
Check your understanding
If the newest pair is too large, should the function skip it and include an older pair? The current policy chooses a contiguous suffix of completed pairs, so it stops. This can omit all history even when a smaller older pair would fit. Explain that tradeoff; do not silently change it during the count-limit exercise.