Skip to content

Conversation compression utility API #8

Description

@jasperblues

Summary

Provide a utility API for producing compressed representations of conversation threads, plus composable operations on top of those representations.

Motivation

Long conversations create several recurring needs:

  • Context window management — agents need a condensed view of history when the raw transcript exceeds token budget.
  • Session summaries — UI surfaces (session list, previews) benefit from a short descriptive summary beyond the auto-generated title.
  • Embedding source material — session-level vector search (see Vector search over chat messages #7) wants a single coherent text to embed rather than concatenated messages.
  • Handoff / resume — agents picking up a thread, or a different agent taking over, need a synopsis of what's happened so far.

Today each consumer has to roll its own summarization logic against the raw List<SimpleStoredMessage>.

Proposed approach

A pluggable ConversationCompressor interface plus a persisted CompressedThread artifact:

interface ConversationCompressor {
    fun compress(messages: List<Message>, strategy: CompressionStrategy): CompressedThread
}

data class CompressedThread(
    val summary: String,              // free-text synopsis
    val keyFacts: List<String>,       // extracted facts / decisions
    val openQuestions: List<String>,  // unresolved threads
    val sourceMessageIds: List<String>,
    val compressedAt: Instant,
    val tokenCount: Int?
)

Compression strategies so callers pick the shape they need:

  • SUMMARY_ONLY — cheapest, for UI previews
  • STRUCTURED — summary + facts + open questions, for agent handoff
  • ROLLING — keep last N messages verbatim, summarize the rest (context-window management)

Utility operations composable on CompressedThread:

  • Persist as a node on the session (HAS_COMPRESSION relationship) with a version/generation counter so we can recompress as the thread grows.
  • Expose on the repository: getLatestCompression(sessionId), compressAndStore(sessionId, strategy).
  • Convenience for agents: Conversation.asCompressedMessages(maxTokens) — returns a message list where older turns are collapsed into a single system message.

Open questions

  • Recompression trigger: on every Nth message, on time elapsed, or on explicit request only?
  • Should compressions be per-strategy (store multiple) or one canonical structured form that consumers project from?
  • Does compression happen on the event bus (same async pattern as embeddings) or on-demand?
  • Reuse the existing TitleGenerator LLM plumbing, or a separate abstraction?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions