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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions