ConversationSegmenter

class ConversationSegmenter(maxSize: Int = DEFAULT_MAX_SIZE, settle: Duration = DEFAULT_SETTLE, minItems: Int = 1)

Splits a timestamped item stream (e.g. a chat channel's messages) into conversation segments, so each segment can be handed to IncrementalSource-based extraction as one conversation.

Brief silence is deliberately not a conversation boundary. In async chat a thread routinely goes quiet for eight or twenty-four hours while contributors sleep across timezones, and cutting on that silence would shred exactly the long-running conversations most worth extracting. Within a conversation, segments are bounded by size: a run is cut only once it exceeds maxSize, and the cut lands on the largest interior gap, so the seam falls in a lull rather than mid-exchange.

A silence longer than settle is a boundary, because settle is already the point at which a trailing conversation is declared finished and handed to extraction. Once a conversation has been extracted, a later reply must begin a new segment rather than re-open the extracted one — so the same duration necessarily governs both.

This decides conversation boundaries; it deliberately does NOT window by size or overlap — that is the incremental analyzer's job (see WindowConfig), which chunks within a segment. Those windows, not whole segments, are what an LLM sees.

A segment is closed — safe to extract — once further content follows it, or, for the trailing segment, once it has been quiet for longer than settle. The still-open trailing segment is withheld so an in-progress conversation isn't extracted and then re-extracted when the rest arrives.

Pure and deterministic — no LLM, no I/O. Generic over the item type: the caller supplies a timestamp accessor and, optionally, a size accessor. Segmentation of a given prefix is stable as later items arrive, so repeated calls over a growing stream emit each closed segment exactly once — provided items are ingested in timestamp order. An item back-dated to within settle of an already-extracted segment will re-open it.

Parameters

maxSize

ceiling on a single segment, expressed in whatever units the size accessor passed to closedSegments returns. Under the default accessor (one unit per item) it is a message count; a caller supplying a token estimator must scale this accordingly. A segment absorbing an undersized tail (see minItems) may exceed it by up to minItems - 1 items: maxSize is a coherence cap, not a hard limit.

settle

how long a conversation must be quiet before it is taken to be finished: the trailing segment becomes extractable, and any later item starts a new segment.

minItems

segments holding fewer items than this are dropped. Cuts are placed so that no segment falls below the floor, and an undersized tail is folded back into the segment before it, so only a whole conversation shorter than minItems is dropped. Setting minItems above the number of items maxSize admits leaves every segment below the floor, discarding the stream entirely.

Constructors

Link copied to clipboard
constructor(maxSize: Int = DEFAULT_MAX_SIZE, settle: Duration = DEFAULT_SETTLE, minItems: Int = 1)

Types

Link copied to clipboard
object Companion

Functions

Link copied to clipboard
fun <T> closedSegments(items: List<T>, now: Instant, timestamp: (T) -> Instant, size: (T) -> Int = { 1 }): List<List<T>>

The closed conversation segments in items (a single stream, e.g. one channel), each a time-ordered sub-list. now is the reference "current time" used to decide whether the trailing segment has settled; a now preceding the last item leaves that segment open. size weights each item against maxSize; the default counts items. Both accessors are invoked exactly once per item.