Conversation Agent History: Paginated Fetching, Then Token-Limit Compaction
Company: Sierra
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
You are building the message-history layer of a conversation agent: the component that holds the conversation's messages and hands them to the language model. The exercise came in parts, and two of them are reported here: loading the history through pagination, and compacting it when it grows past a token limit. The exercise continued with further parts that are not described.
A possible interface (assumed; adjust it as you clarify):
```python
from dataclasses import dataclass
@dataclass(frozen=True)
class Message:
id: str
role: str # for example "user" or "assistant"
text: str
tokens: int # token count of this message
# The history is served by a paginated source. Each call returns one page of
# messages and the cursor of the next page, or None after the last page.
def fetch_page(cursor: str | None) -> tuple[list[Message], str | None]: ...
```
### Constraints and Clarifications
- Each message's token count is given; you do not need to tokenize text.
- The token limit is a positive integer chosen by the caller.
### Clarifying Questions
- Does the source return pages oldest-first or newest-first, and in what order are the messages within a page?
- Should the history be loaded eagerly into a list, or fetched lazily, one page at a time, as the caller iterates?
- Can a fetch fail or time out, and should it be retried?
- Is a total exactly equal to the limit acceptable?
- What if a single message alone exceeds the limit? Must a leading system message, or a question together with its answer, be kept or dropped as a unit?
### Part 1 — Pagination
Write the code that fetches the conversation from the paginated source one page at a time and assembles a single list of messages in conversation order.
```hint Know when to stop
Decide exactly what tells your loop that there are no more pages, and what it should do if the source misbehaves, for example by returning a cursor it has already returned.
```
#### What This Part Should Cover
- A correct loop over pages with a clear termination condition
- The order of messages across pages and within a page
- Handling of empty pages, transient failures and a misbehaving cursor
- Eager versus lazy loading
### Part 2 — Compaction
Every message has a token count. When the total number of tokens in the history exceeds the limit, drop messages starting from the oldest until the history fits. Make compaction cheap enough to run after every new message.
```hint Avoid repeated work
Re-summing the whole history after each new message is wasteful, and so is removing items from the front of a structure that shifts every remaining element.
```
#### What This Part Should Cover
- Correct trimming: the oldest messages leave first, and only as many as needed
- The cost per new message, and the structure used for removal from the front
- Edge cases: a single oversized message, an empty history, messages that must be kept
- When compaction runs relative to calls to the model
### What a Strong Answer Covers
- Clarifies page order, retry behavior and the compaction policy before coding
- A pagination loop that always terminates and preserves conversation order
- Compaction that keeps a running total and removes from the front in constant time
- Explicit handling of edge cases instead of silently losing the newest message
- Readable, testable code, with tests for both parts
### Follow-up Questions
- Instead of discarding old messages, the product wants them summarized. How does compaction change, and how do you budget for the summary's own tokens?
- A tool call and its result must always be kept or dropped together. How does your compaction respect that?
- New messages arrive while you are still paginating through older ones. How do you avoid duplicates and gaps?
- How would you test the pagination code without a live service?
Overview: Build the message-history layer of a conversation agent: fetch the conversation page by page into one ordered list, then compact it whenever the total token count exceeds a limit by dropping the oldest messages. It tests pagination termination and ordering, running totals, and cheap removal from the front.