Documentation
¶
Overview ¶
Package history manages conversation transcripts: append messages, load them back fitted to a model's context window, and (optionally) compact older turns into hierarchical summaries to keep that window finite. Long-term fact recall lives in sdk/recall.
Layering ¶
- History is the strategy interface returned by NewBuffer and NewCompacted. Pick Buffer for short sessions or tests; pick Compacted when a conversation needs to outgrow a single context window.
- Coordinator is the lifecycle + maintenance interface that the compacted History additionally satisfies. It exposes Compact / Archive / Shutdown and serializes them per conversation against History.Append and the background ingest/archive worker.
- Store is the persistence interface. The package ships InMemoryStore and NewFileStore; bring your own for Redis, Postgres, etc.
- SummaryDAG / FileSummaryStore are the building blocks behind compacted's hierarchical summarization. Most callers do not touch them directly.
Lifecycle ¶
NewBuffer returns a stateless History; nothing to drain. The History returned by NewCompacted owns one serial worker goroutine per conversation plus a startup archive-recovery goroutine. Callers that own its lifetime should type-assert to Coordinator and call Shutdown(ctx) on shutdown so the queues drain cleanly:
hist := history.NewCompacted(store, llm, ws)
if coord, ok := hist.(history.Coordinator); ok {
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = coord.Shutdown(ctx)
}()
}
Tools ¶
Two graph tools surface this package to LLMs: history_expand fetches the verbatim messages behind a summary node, history_compact triggers a manual compaction. The in-tree tool wrappers (taking a Coordinator for per-conversation serialization) live in an adapter package outside sdk; this package exposes only the underlying primitives.
v0.3.0 surface ¶
The v0.3 surface narrows on the History / Coordinator interfaces. The following v0.2 entry points were removed in v0.3.0:
- Closer / compactor.Close — replaced by Coordinator.Shutdown (context-aware, refuses late writes).
- Top-level RecoverArchive, SaveManifest helpers — folded into the internal recoverArchiveImpl/saveManifestImpl helpers exercised by Coordinator. The exported Archive, LoadManifest and LoadArchivedMessages survive for adapter packages that need direct archive access; Coordinator auto-recovers in-flight archives at construction.
- SummaryCacheStore — superseded by SummaryStore which the DAG already consumes.
- In-package ToolDeps / RegisterTools — moved out of sdk into the adapter layer.
Naming history ¶
This package was renamed from sdk/memory in v0.2.0. The previous Save(fullHistory) method was replaced by Append(newOnly) — the old signature was lossy under concurrent writers (read-modify-write race) and silently accepted truncated histories. There is no compat shim; call sites pass only the freshly produced messages.
Index ¶
- Variables
- func ApplyLoadOptions(msgs []model.Message, opts LoadOptions) []model.Message
- func BuildSummaryIndex(ctx context.Context, store SummaryStore, convID string, budget int) string
- func ConversationIDFrom(ctx context.Context) string
- func LoadArchivedMessages(ctx context.Context, ws workspace.Workspace, ...) ([]model.Message, error)
- func LoadFiltered(ctx context.Context, h History, conversationID string, opts LoadOptions) ([]model.Message, error)
- func NewSummaryNodeID() string
- func WithConversationID(ctx context.Context, id string) context.Context
- type ArchiveConfig
- type ArchiveManifest
- type ArchiveResult
- type ArchiveSegment
- type Budget
- type BufferOption
- type CompactConfig
- type CompactOption
- func WithArchiveBatchSize(n int) CompactOption
- func WithArchiveThreshold(n int) CompactOption
- func WithChunkSize(n int) CompactOption
- func WithCompactThreshold(n int) CompactOption
- func WithCondenseThreshold(n int) CompactOption
- func WithDAGConfig(cfg DAGConfig) CompactOption
- func WithLeafPrune(b bool) CompactOption
- func WithMaxDepth(n int) CompactOption
- func WithRecentRatio(r float64) CompactOption
- func WithStoragePrefix(p string) CompactOption
- func WithTokenBudget(n int) CompactOption
- func WithTokenCounter(c TokenCounter) CompactOption
- type CompactResult
- type Coordinator
- type DAGConfig
- type EstimateCounter
- type FileStore
- func (s *FileStore) DeleteMessages(ctx context.Context, conversationID string) error
- func (s *FileStore) GetMessageRange(ctx context.Context, conversationID string, start, end int) ([]model.Message, error)
- func (s *FileStore) GetMessages(ctx context.Context, conversationID string) ([]model.Message, error)
- func (s *FileStore) SaveMessages(ctx context.Context, conversationID string, messages []model.Message) error
- type FileSummaryStore
- func (s *FileSummaryStore) DeleteByConvID(ctx context.Context, convID, id string) error
- func (s *FileSummaryStore) GetByConvID(ctx context.Context, convID, id string) (*SummaryNode, error)
- func (s *FileSummaryStore) List(ctx context.Context, convID string, opts SummaryListOptions) ([]*SummaryNode, error)
- func (s *FileSummaryStore) ListAll(ctx context.Context, convID string) ([]*SummaryNode, error)
- func (s *FileSummaryStore) Rewrite(ctx context.Context, convID string, nodes []*SummaryNode) error
- func (s *FileSummaryStore) Save(ctx context.Context, node *SummaryNode) error
- func (s *FileSummaryStore) Search(ctx context.Context, convID, query string, topK int) ([]*SummaryNode, error)
- type FileSummaryStoreOption
- type FilterableHistory
- type History
- type InMemoryStore
- func (s *InMemoryStore) AppendMessages(_ context.Context, conversationID string, messages []model.Message) error
- func (s *InMemoryStore) Close()
- func (s *InMemoryStore) DeleteMessages(_ context.Context, conversationID string) error
- func (s *InMemoryStore) GetMessages(_ context.Context, conversationID string) ([]model.Message, error)
- func (s *InMemoryStore) Len() int
- func (s *InMemoryStore) SaveMessages(_ context.Context, conversationID string, messages []model.Message) error
- type InMemoryStoreOption
- type LoadOptions
- type MessageAppender
- type RangeReader
- type RecentReader
- type Store
- type SummaryDAG
- func (d *SummaryDAG) Assemble(ctx context.Context, convID string, tokenBudget int) ([]llm.Message, error)
- func (d *SummaryDAG) Compact(ctx context.Context, convID string) (CompactResult, error)
- func (d *SummaryDAG) Ingest(ctx context.Context, convID string, messages []llm.Message, startSeq int) error
- type SummaryListOptions
- type SummaryNode
- type SummaryStore
- type TiktokenCounter
- type TokenCounter
Constants ¶
This section is empty.
Variables ¶
var ErrClosed = errors.New("history: coordinator closed")
ErrClosed is returned by Coordinator.Shutdown's downstream operations (Append, Compact, Archive) once Shutdown has been initiated. Callers observing this should treat the History as drained and avoid further writes; reading via Load remains valid as long as the underlying Store is still usable.
Functions ¶
func ApplyLoadOptions ¶
func ApplyLoadOptions(msgs []model.Message, opts LoadOptions) []model.Message
ApplyLoadOptions filters msgs in memory according to opts. It is the reference implementation reused by both the in-memory fallback in LoadFiltered and any FilterableHistory implementation that prefers to do its own Load and then defer filtering to the shared helper.
The returned slice is a fresh allocation; the input slice is not mutated. msgs[i] are NOT cloned — IncludeTools=false strips tool-bearing Parts via model.Message.Clone only on the messages that actually carry them, so callers can rely on the returned slice being safe to retain.
func BuildSummaryIndex ¶
BuildSummaryIndex generates a summary index string from the top-level summaries of a conversation. The result is intended to be injected into the LLM system prompt via the workflow.VarSummaryIndex board variable.
Returns an empty string when no summaries exist or the store is nil. The budget parameter controls the maximum character length of the output; older summaries are omitted (with a note) when the budget is exceeded.
func ConversationIDFrom ¶
ConversationIDFrom retrieves the conversation ID from the context.
func LoadArchivedMessages ¶
func LoadArchivedMessages(ctx context.Context, ws workspace.Workspace, prefix, archivePrefix, convID string, startSeq, endSeq int) ([]model.Message, error)
LoadArchivedMessages reads messages from gzip archive segments. It powers history_expand's cold-segment path; callers outside the history package should obtain archived turns via the history_expand tool wrapper, not by reading archive files directly.
func LoadFiltered ¶
func LoadFiltered(ctx context.Context, h History, conversationID string, opts LoadOptions) ([]model.Message, error)
LoadFiltered returns the messages selected by opts. If h satisfies FilterableHistory the call is delegated to it; otherwise this helper performs a plain h.Load(ctx, conversationID, opts.Budget) and applies the filters in memory.
LoadFiltered is the recommended entry point for callers that need post-Load filtering: it picks the most efficient path automatically and shields callers from the FilterableHistory type assertion.
func NewSummaryNodeID ¶
func NewSummaryNodeID() string
NewSummaryNodeID generates a unique ID for a summary node.
Types ¶
type ArchiveConfig ¶
ArchiveConfig controls message archiving behavior.
type ArchiveManifest ¶
type ArchiveManifest struct {
Segments []ArchiveSegment `json:"segments"`
HotStartSeq int `json:"hot_start_seq"`
}
ArchiveManifest tracks archived message segments.
func LoadManifest ¶
func LoadManifest(ctx context.Context, ws workspace.Workspace, prefix, archivePrefix, convID string) (*ArchiveManifest, error)
LoadManifest reads the archive manifest for a conversation.
type ArchiveResult ¶
type ArchiveResult struct {
MessagesArchived int `json:"messages_archived"`
ArchiveFile string `json:"archive_file,omitempty"`
HotStartSeq int `json:"hot_start_seq"`
}
ArchiveResult holds the result of an archive operation.
func Archive ¶
func Archive(ctx context.Context, ws workspace.Workspace, store Store, prefix, convID string, cfg ArchiveConfig) (ArchiveResult, error)
Archive moves old messages to gzip-compressed archive files. The Coordinator drives it through its per-conversation worker queue; LLM tools (history_compact in particular) call it directly when the caller has not wired a Coordinator.
Crash recovery is handled by recoverArchiveImpl, which the Coordinator runs lazily on first contact with a conversation. Callers that own a History from NewCompacted should always reach archive through Coordinator.Archive to inherit the per-conversation serialization that protects against racing Append/trim sequences.
type ArchiveSegment ¶
type ArchiveSegment struct {
File string `json:"file"`
StartSeq int `json:"start_seq"`
EndSeq int `json:"end_seq"`
Count int `json:"count"`
CreatedAt time.Time `json:"created_at"`
}
ArchiveSegment describes a single archived file.
type Budget ¶
type Budget struct {
// MaxTokens caps the estimated token count of returned messages.
// Implementations that do not track tokens treat this as a hint.
MaxTokens int
// MaxMessages caps the raw message count.
MaxMessages int
}
Budget caps how much transcript History.Load returns. Zero means "use the implementation default"; set either field to clamp.
type BufferOption ¶
type BufferOption func(*buffer)
BufferOption customizes a History built by NewBuffer.
func WithBufferMax ¶
func WithBufferMax(n int) BufferOption
WithBufferMax sets the maximum message count kept in the returned History. Must be > 0; values ≤ 0 are ignored and the default (50) is kept.
type CompactConfig ¶
CompactConfig controls the compact behavior.
type CompactOption ¶
type CompactOption func(*compactOptions)
CompactOption customizes a History built by NewCompacted.
Compaction knobs (chunk size, recent ratio, leaf pruning, archive threshold, …) used to live in a dedicated Config struct; they are now functional options so adding a new knob does not break every caller passing a struct literal.
func WithArchiveBatchSize ¶
func WithArchiveBatchSize(n int) CompactOption
WithArchiveBatchSize sets how many messages move per archive run.
func WithArchiveThreshold ¶
func WithArchiveThreshold(n int) CompactOption
WithArchiveThreshold sets the hot-message count that triggers archival of the oldest batch to cold storage.
func WithChunkSize ¶
func WithChunkSize(n int) CompactOption
WithChunkSize sets how many messages feed into each leaf summary.
func WithCompactThreshold ¶
func WithCompactThreshold(n int) CompactOption
WithCompactThreshold triggers compaction once the hot message count crosses this number.
func WithCondenseThreshold ¶
func WithCondenseThreshold(n int) CompactOption
WithCondenseThreshold sets the sibling-count that triggers a depth+1 condensation.
func WithDAGConfig ¶
func WithDAGConfig(cfg DAGConfig) CompactOption
WithDAGConfig overrides the entire DAGConfig used by the DAG summarizer. Individual knobs below compose on top of a default config; use this when you need to set many at once or inherit from a DefaultDAGConfig.
func WithLeafPrune ¶
func WithLeafPrune(b bool) CompactOption
WithLeafPrune turns on/off deleting the leaf content after its summary is absorbed into a parent node.
func WithMaxDepth ¶
func WithMaxDepth(n int) CompactOption
WithMaxDepth caps the summary tree height.
func WithRecentRatio ¶
func WithRecentRatio(r float64) CompactOption
WithRecentRatio splits the token budget between "recent verbatim messages" and "older summaries".
func WithStoragePrefix ¶
func WithStoragePrefix(p string) CompactOption
WithStoragePrefix sets the workspace prefix for summary/archive files. Default "memory" for backwards compatibility with files written by prior builds.
func WithTokenBudget ¶
func WithTokenBudget(n int) CompactOption
WithTokenBudget caps the assembled context size in estimated tokens.
func WithTokenCounter ¶
func WithTokenCounter(c TokenCounter) CompactOption
WithTokenCounter swaps the TokenCounter used during assembly. Defaults to EstimateCounter.
type CompactResult ¶
type CompactResult struct {
DeletedRemoved int `json:"deleted_removed"`
LeafPruned int `json:"leaf_pruned"`
TotalRemaining int `json:"total_remaining"`
}
CompactResult holds the result of a compact operation.
type Coordinator ¶
type Coordinator interface {
// Compact runs DAG compact for one conversation. Serialized against
// concurrent Append/Archive on the same conversationID.
Compact(ctx context.Context, conversationID string) (CompactResult, error)
// Archive runs message archiving for one conversation. Serialized
// against concurrent Append/Compact on the same conversationID.
Archive(ctx context.Context, conversationID string) (ArchiveResult, error)
// Shutdown stops accepting new work and waits for in-flight per-
// conversation queues to drain. After Shutdown is observed,
// Append/Compact/Archive return [ErrClosed]; subsequent Shutdown
// calls block on the same drain and return its result.
//
// Shutdown is the canonical "S3" semantics: it does NOT cancel the
// background workers when ctx expires. A ctx-deadline return value
// signals "drain in progress, partial work may still finalize after
// this returns"; the Coordinator stays in the closing/closed state
// regardless. To observe the eventual drain after a deadline-bounded
// Shutdown, call Shutdown again with a longer (or unbounded) ctx —
// the second call falls onto the same drain and returns nil once
// all workers have exited.
Shutdown(ctx context.Context) error
}
Coordinator is the lifecycle + maintenance interface that the History returned by NewCompacted additionally satisfies. All maintenance operations (compact, archive, shutdown) are serialized per conversation alongside History.Append, so callers no longer need to coordinate locks themselves.
Production callers typically grab it once at construction:
hist := history.NewCompacted(store, llm, ws)
coord, _ := hist.(history.Coordinator)
defer func() { _ = coord.Shutdown(context.Background()) }()
Coordinator offers context-aware shutdown plus first-class maintenance entry points (Compact / Archive) that internally share the per-conversation queue used by background ingest/archive.
type DAGConfig ¶
type DAGConfig struct {
ChunkSize int
CondenseThreshold int
CondenseGroupSize int
MaxDepth int
TokenBudget int
RecentRatio float64
MidRatio float64
Compact CompactConfig
Archive ArchiveConfig
}
DAGConfig controls the summary DAG behavior.
func DefaultDAGConfig ¶
func DefaultDAGConfig() DAGConfig
DefaultDAGConfig returns a DAGConfig with sensible defaults.
type EstimateCounter ¶
type EstimateCounter struct{}
EstimateCounter uses a heuristic: ~4 ASCII chars/token, ~1.5 CJK chars/token.
func (*EstimateCounter) Count ¶
func (c *EstimateCounter) Count(text string) int
func (*EstimateCounter) CountMessages ¶
func (c *EstimateCounter) CountMessages(msgs []model.Message) int
type FileStore ¶
type FileStore struct {
// contains filtered or unexported fields
}
FileStore is a Workspace-backed Store that persists messages as JSONL files. Each conversation is stored at {prefix}/{conversationID}/messages.jsonl with one JSON-encoded model.Message per line. Saves are incremental.
func NewFileStore ¶
NewFileStore creates a FileStore rooted at the given prefix directory within the workspace (e.g. "memory").
func (*FileStore) DeleteMessages ¶
func (*FileStore) GetMessageRange ¶
func (s *FileStore) GetMessageRange(ctx context.Context, conversationID string, start, end int) ([]model.Message, error)
GetMessageRange returns messages in the range [start, end).
func (*FileStore) GetMessages ¶
type FileSummaryStore ¶
type FileSummaryStore struct {
// contains filtered or unexported fields
}
FileSummaryStore is a Workspace-backed SummaryStore using JSONL files. It caches parsed nodes per conversation to avoid repeated disk reads.
The cache is bounded (LRU): the previous implementation grew the in-memory map for every conversation ever touched, which leaked memory in long-running services; here we evict the least-recently-used conversation when capacity is exceeded.
func NewFileSummaryStore ¶
func NewFileSummaryStore(ws workspace.Workspace, prefix string, opts ...FileSummaryStoreOption) *FileSummaryStore
NewFileSummaryStore creates a FileSummaryStore rooted at the given prefix.
func (*FileSummaryStore) DeleteByConvID ¶
func (s *FileSummaryStore) DeleteByConvID(ctx context.Context, convID, id string) error
func (*FileSummaryStore) GetByConvID ¶
func (s *FileSummaryStore) GetByConvID(ctx context.Context, convID, id string) (*SummaryNode, error)
func (*FileSummaryStore) List ¶
func (s *FileSummaryStore) List(ctx context.Context, convID string, opts SummaryListOptions) ([]*SummaryNode, error)
func (*FileSummaryStore) ListAll ¶
func (s *FileSummaryStore) ListAll(ctx context.Context, convID string) ([]*SummaryNode, error)
func (*FileSummaryStore) Rewrite ¶
func (s *FileSummaryStore) Rewrite(ctx context.Context, convID string, nodes []*SummaryNode) error
func (*FileSummaryStore) Save ¶
func (s *FileSummaryStore) Save(ctx context.Context, node *SummaryNode) error
func (*FileSummaryStore) Search ¶
func (s *FileSummaryStore) Search(ctx context.Context, convID, query string, topK int) ([]*SummaryNode, error)
type FileSummaryStoreOption ¶
type FileSummaryStoreOption func(*FileSummaryStore)
FileSummaryStoreOption configures a FileSummaryStore.
func WithSummaryStoreCapacity ¶
func WithSummaryStoreCapacity(n int) FileSummaryStoreOption
WithSummaryStoreCapacity overrides the default LRU cache capacity. A value <= 0 leaves the default in place.
type FilterableHistory ¶
type FilterableHistory interface {
History
// LoadFiltered returns messages for the next inspection / display
// the same way [History.Load] does, additionally honouring
// LoadOptions. Implementations MAY apply filters lazily (push down
// to the store) or eagerly (call Load + filter); both are correct
// as long as the result respects LoadOptions semantics documented
// above.
LoadFiltered(ctx context.Context, conversationID string, opts LoadOptions) ([]model.Message, error)
}
FilterableHistory is the optional sub-interface that History implementations can satisfy to honour LoadOptions efficiently. For example, a store-backed History may push role filtering down to the database instead of materialising the whole conversation.
History implementations that do NOT satisfy FilterableHistory still work with LoadFiltered: the helper falls back to a plain Load and applies the filters in memory. This keeps adding the new entry point non-breaking for downstream code that built its own History.
type History ¶
type History interface {
// Load returns messages suited for the next LLM call. Implementations
// MAY compress, summarize, or window the underlying transcript.
//
// budget is a hint; implementations fall back to their configured
// defaults when the corresponding [Budget] field is zero. A fully
// zero Budget explicitly means "use defaults" and is the most
// common value (the common case: "give me whatever you'd send to
// the model").
Load(ctx context.Context, conversationID string, budget Budget) ([]model.Message, error)
// Append durably persists newMessages — and only newMessages — to the
// conversation. It MUST be safe to call from multiple goroutines for
// the same conversationID; implementations serialize per-conversation
// writes internally. After Append returns nil, the messages are
// guaranteed visible to subsequent Load calls.
Append(ctx context.Context, conversationID string, newMessages []model.Message) error
// Clear removes the conversation and any derived state (summaries,
// archives) owned by this History implementation.
Clear(ctx context.Context, conversationID string) error
}
History is the strategy-layer interface that decides which messages to return to the LLM and how new turns are persisted. See the package doc comment for the per-method contract.
func NewBuffer ¶
func NewBuffer(store Store, opts ...BufferOption) History
NewBuffer returns a History that keeps the most recent messages for each conversation up to a cap (default 50; override with WithBufferMax).
It is the simplest History implementation — appends concatenate, loads truncate. Use it for short sessions, tests, and examples; switch to NewCompacted when a conversation needs to outgrow a single context window.
func NewCompacted ¶
NewCompacted returns a History that keeps the full transcript but summarizes older turns through a DAG to stay within a token budget. Requires both an LLM (for summarization) and a workspace.Workspace (for summary + archive persistence).
This is the recommended default for any agent that holds multi-session conversations; use NewBuffer for short or single-turn interactions.
type InMemoryStore ¶
type InMemoryStore struct {
// contains filtered or unexported fields
}
InMemoryStore is a simple in-memory message store for development and testing. It supports a maximum conversation count and TTL-based eviction.
func NewInMemoryStore ¶
func NewInMemoryStore(opts ...InMemoryStoreOption) *InMemoryStore
NewInMemoryStore creates a new in-memory message store with optional limits.
func (*InMemoryStore) AppendMessages ¶
func (s *InMemoryStore) AppendMessages(_ context.Context, conversationID string, messages []model.Message) error
AppendMessages implements MessageAppender atomically under s.mu so concurrent same-conversation Append calls cannot read-modify- write each other's batches. The pre-fix path (no MessageAppender implementation here) forced callers into a manual GetMessages+SaveMessages loop where the two sides were unlocked between calls — issue #154's documented failure mode.
Same eviction rules as [SaveMessages]: a new conversation that pushes len(s.data) past maxConversations triggers LRU eviction.
func (*InMemoryStore) Close ¶
func (s *InMemoryStore) Close()
Close stops the background cleanup goroutine.
func (*InMemoryStore) DeleteMessages ¶
func (s *InMemoryStore) DeleteMessages(_ context.Context, conversationID string) error
func (*InMemoryStore) GetMessages ¶
func (*InMemoryStore) Len ¶
func (s *InMemoryStore) Len() int
Len returns the number of stored conversations (useful for testing/monitoring).
func (*InMemoryStore) SaveMessages ¶
type InMemoryStoreOption ¶
type InMemoryStoreOption func(*InMemoryStore)
InMemoryStoreOption configures an InMemoryStore.
func WithMaxConversations ¶
func WithMaxConversations(n int) InMemoryStoreOption
WithMaxConversations sets the upper bound on stored conversations. When exceeded, the least-recently-accessed conversation is evicted.
func WithTTL ¶
func WithTTL(d time.Duration) InMemoryStoreOption
WithTTL sets how long an idle conversation is kept before eviction.
type LoadOptions ¶
type LoadOptions struct {
Budget Budget
Roles []model.Role
SinceSeq int
LimitN int
IncludeTools bool
}
LoadOptions filters the messages returned by LoadFiltered / a FilterableHistory implementation. Zero values mean "no filter on this dimension" — the empty LoadOptions is identical to a plain Load with Budget==zero.
LoadOptions is the moderation-friendly counterpart to Budget: where Budget caps how much transcript reaches the LLM, LoadOptions shapes which slice of the transcript is read for inspection, debugging, audit views, or selective replay.
Filter semantics are evaluated AFTER the underlying History strategy returns its working set:
- Budget: forwarded to the underlying Load. Implementations apply compaction / windowing the same way they always do.
- Roles: if non-empty, only messages whose Role is in the set are kept. The empty set means "all roles".
- SinceSeq: 0-based message sequence index. Messages at index < SinceSeq are dropped. SinceSeq is a position cutoff because model.Message does not carry a wall-clock timestamp; callers wanting a time cutoff should look up the sequence index in their own audit log first. SinceSeq is applied against the position in the slice returned by Load (i.e. AFTER compaction), so it is stable for callers that always pass the same Budget. Negative values are treated as 0.
- LimitN: caps the number of messages returned AFTER role + SinceSeq filtering. 0 means "no cap"; the most recent LimitN surviving messages are kept (tail-biased to match the typical "show me the last N user/assistant turns" use case).
- IncludeTools: when false (the default), strips tool-call / tool-result parts as well as RoleTool messages from the result. This matches the common moderation case where reviewers want the human-readable conversation only. When true, tool messages and tool-call parts are preserved verbatim.
Filters compose: callers can mix Roles + LimitN to e.g. "give me the last 20 assistant messages". The empty LoadOptions is a no-op and returns whatever the underlying Load produces.
func (LoadOptions) IsZero ¶
func (opts LoadOptions) IsZero() bool
IsZero reports whether opts carries no explicit filters and is equivalent to a plain Load with the zero Budget.
type MessageAppender ¶
type MessageAppender interface {
AppendMessages(ctx context.Context, conversationID string, messages []model.Message) error
}
MessageAppender is an optional interface for stores that can append only the newly generated messages without reloading the full history.
type RangeReader ¶
type RangeReader interface {
GetMessageRange(ctx context.Context, conversationID string, start, end int) ([]model.Message, error)
}
RangeReader is an optional interface for stores that support reading a subset of messages by sequence range.
type RecentReader ¶
type RecentReader interface {
GetRecentMessages(ctx context.Context, conversationID string, limit int) ([]model.Message, error)
}
RecentReader is an optional interface for stores that can efficiently read only the most recent N messages for a conversation.
type Store ¶
type Store interface {
GetMessages(ctx context.Context, conversationID string) ([]model.Message, error)
SaveMessages(ctx context.Context, conversationID string, messages []model.Message) error
DeleteMessages(ctx context.Context, conversationID string) error
}
Store is the persistence-layer interface for short-term message storage.
type SummaryDAG ¶
type SummaryDAG struct {
// contains filtered or unexported fields
}
SummaryDAG manages the multi-layer summary DAG for a conversation.
func NewSummaryDAG ¶
func NewSummaryDAG(store SummaryStore, msgStore Store, l llm.LLM, cfg DAGConfig, counter TokenCounter) *SummaryDAG
NewSummaryDAG creates a new SummaryDAG.
func (*SummaryDAG) Assemble ¶
func (d *SummaryDAG) Assemble(ctx context.Context, convID string, tokenBudget int) ([]llm.Message, error)
Assemble constructs the context window from summaries + recent messages.
func (*SummaryDAG) Compact ¶
func (d *SummaryDAG) Compact(ctx context.Context, convID string) (CompactResult, error)
Compact removes deleted nodes and prunes leaf content.
type SummaryListOptions ¶
SummaryListOptions controls List filtering.
type SummaryNode ¶
type SummaryNode struct {
ID string `json:"id"`
ConversationID string `json:"conversation_id"`
Depth int `json:"depth"`
Content string `json:"content"`
ExpandHint string `json:"expand_hint,omitempty"`
SourceIDs []string `json:"source_ids,omitempty"`
EarliestSeq int `json:"earliest_seq"`
LatestSeq int `json:"latest_seq"`
TokenCount int `json:"token_count"`
CreatedAt time.Time `json:"created_at"`
Deleted bool `json:"deleted,omitempty"`
}
SummaryNode represents a node in the summary DAG.
type SummaryStore ¶
type SummaryStore interface {
Save(ctx context.Context, node *SummaryNode) error
GetByConvID(ctx context.Context, convID, id string) (*SummaryNode, error)
List(ctx context.Context, convID string, opts SummaryListOptions) ([]*SummaryNode, error)
Search(ctx context.Context, convID, query string, topK int) ([]*SummaryNode, error)
DeleteByConvID(ctx context.Context, convID, id string) error
ListAll(ctx context.Context, convID string) ([]*SummaryNode, error)
Rewrite(ctx context.Context, convID string, nodes []*SummaryNode) error
}
SummaryStore persists and retrieves summary DAG nodes. All operations are scoped by conversation ID.
type TiktokenCounter ¶
type TiktokenCounter struct {
// contains filtered or unexported fields
}
TiktokenCounter uses tiktoken-go for precise BPE token counting.
func NewTiktokenCounter ¶
func NewTiktokenCounter(model string) (*TiktokenCounter, error)
NewTiktokenCounter creates a TiktokenCounter for the given model name (e.g. "gpt-4o", "gpt-4", "gpt-3.5-turbo"). Falls back to cl100k_base encoding if the model is not recognized.
func NewTiktokenCounterFromEncoding ¶
func NewTiktokenCounterFromEncoding(encoding string) (*TiktokenCounter, error)
NewTiktokenCounterFromEncoding creates a TiktokenCounter for a specific encoding name (e.g. "cl100k_base", "o200k_base").
func (*TiktokenCounter) Count ¶
func (c *TiktokenCounter) Count(text string) int
func (*TiktokenCounter) CountMessages ¶
func (c *TiktokenCounter) CountMessages(msgs []model.Message) int