statestore

package
v1.4.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 15 Imported by: 9

Documentation

Overview

Package statestore provides conversation state persistence and management.

Index

Constants

View Source
const (
	SortByCreatedAt = "created_at"
	SortByUpdatedAt = "updated_at"
)

Sort field constants for ListOptions.SortBy.

View Source
const DefaultMaxEntries = 10000

DefaultMaxEntries is the default maximum number of entries a MemoryStore will hold. When the limit is reached, the least-recently-accessed entry is evicted. Use WithNoMaxEntries() to explicitly disable the entry limit.

View Source
const DefaultMaxIndexedConversations = 1000

DefaultMaxIndexedConversations is the default maximum number of conversations that can be indexed in memory before LRU eviction of the least recently used.

View Source
const DefaultTTL = 1 * time.Hour

DefaultTTL is the default time-to-live for conversation states in a MemoryStore. Entries that have not been accessed within the TTL are considered expired and eligible for eviction. Use WithNoTTL() to explicitly disable TTL expiration.

Variables

View Source
var ErrInvalidID = errors.New("invalid conversation ID")

ErrInvalidID is returned when an invalid conversation ID is provided.

View Source
var ErrInvalidState = errors.New("invalid conversation state")

ErrInvalidState is returned when a conversation state is invalid.

View Source
var ErrNotFound = errors.New("conversation not found")

ErrNotFound is returned when a conversation doesn't exist in the store.

Functions

This section is empty.

Types

type BulkWriter added in v1.4.7

type BulkWriter interface {
	// Save persists the entire conversation state, overwriting any existing
	// record. Auto-creates the conversation if it doesn't exist.
	Save(ctx context.Context, state *ConversationState) error
}

BulkWriter allows replacing the entire conversation state in one operation. Optional and explicitly OUT-OF-BAND for hot-path stages — it exists for admin tools, test seeders, and Session.Clear-style ops.

Pipeline stages must NEVER take a BulkWriter as a config field. Use MessageAppender, MetadataAccessor.MergeMetadata, and SummaryAccessor.SaveSummary for typed, race-safe writes instead. A reflective test in runtime/pipeline/stage enforces this invariant.

type ConversationState

type ConversationState struct {
	ID             string                 // Unique conversation identifier
	UserID         string                 // User who owns this conversation
	Messages       []types.Message        // Message history (using unified types.Message)
	SystemPrompt   string                 // System prompt for this conversation
	Summaries      []Summary              // Compressed summaries of old turns
	TokenCount     int                    // Total tokens in messages
	LastAccessedAt time.Time              // Last time conversation was accessed
	Metadata       map[string]interface{} // Arbitrary metadata (e.g., extracted context)
}

ConversationState represents stored conversation state in the state store. This is the primary data structure for persisting and loading conversation history.

type InMemoryIndex added in v1.3.1

type InMemoryIndex struct {
	// contains filtered or unexported fields
}

InMemoryIndex provides an in-memory implementation of MessageIndex using brute-force cosine similarity search. Suitable for development, testing, and conversations with up to ~10K messages.

The number of indexed conversations is bounded by maxConversations with LRU eviction to prevent unbounded memory growth.

func NewInMemoryIndex added in v1.3.1

func NewInMemoryIndex(provider providers.EmbeddingProvider, opts ...InMemoryIndexOption) *InMemoryIndex

NewInMemoryIndex creates a new in-memory message index.

func (*InMemoryIndex) Delete added in v1.3.1

func (idx *InMemoryIndex) Delete(_ context.Context, conversationID string) error

Delete removes all indexed messages for a conversation.

func (*InMemoryIndex) Index added in v1.3.1

func (idx *InMemoryIndex) Index(
	ctx context.Context, conversationID string, turnIndex int, message types.Message,
) error

Index adds a message to the search index by computing its embedding.

func (*InMemoryIndex) Search added in v1.3.1

func (idx *InMemoryIndex) Search(ctx context.Context, conversationID, query string, k int) ([]IndexResult, error)

Search finds the top-k messages most relevant to the query string.

type InMemoryIndexOption added in v1.3.10

type InMemoryIndexOption func(*InMemoryIndex)

InMemoryIndexOption configures an InMemoryIndex.

func WithMaxIndexedConversations added in v1.3.10

func WithMaxIndexedConversations(maxConv int) InMemoryIndexOption

WithMaxIndexedConversations sets the maximum number of conversations to index. Default is DefaultMaxIndexedConversations (1000).

type IndexResult added in v1.3.1

type IndexResult struct {
	// TurnIndex is the position of the message in the conversation history.
	TurnIndex int

	// Message is the full message content.
	Message types.Message

	// Score is the relevance score (higher is more relevant, typically 0.0-1.0).
	Score float64
}

IndexResult represents a single search result from the message index.

type LLMSummarizer added in v1.3.1

type LLMSummarizer struct {
	// contains filtered or unexported fields
}

LLMSummarizer uses an LLM provider to compress messages into summaries.

func NewLLMSummarizer added in v1.3.1

func NewLLMSummarizer(provider providers.Provider) *LLMSummarizer

NewLLMSummarizer creates a new LLM-based summarizer. A cheaper/faster model is recommended (e.g., GPT-3.5, Claude Haiku).

func (*LLMSummarizer) Summarize added in v1.3.1

func (s *LLMSummarizer) Summarize(ctx context.Context, messages []types.Message) (string, error)

Summarize compresses the given messages into a concise summary.

type ListAccessor added in v1.4.7

type ListAccessor interface {
	// AppendList appends items to the named list for the conversation.
	// Items are opaque bytes (typically JSON-encoded by the caller).
	// Auto-creates the conversation and the list if either is missing.
	AppendList(ctx context.Context, id, listName string, items [][]byte) error

	// LoadList returns all items of the named list, in append order.
	// Returns (nil, nil) — not an error — when the list is empty or
	// has never been written. Returns ErrNotFound only when the
	// conversation itself doesn't exist.
	LoadList(ctx context.Context, id, listName string) ([][]byte, error)

	// ListLen returns the current length of the named list. Returns 0
	// for empty/missing lists; ErrNotFound only when the conversation
	// doesn't exist.
	ListLen(ctx context.Context, id, listName string) (int, error)
}

ListAccessor allows storing append-only collections of opaque items (JSON-encoded by the caller) per conversation. Each list is keyed by a stable name (e.g. "workflow.history").

Stores that implement this interface persist appends incrementally — MemoryStore in a Go slice, RedisStore as a Redis list (RPUSH). This keeps per-write cost O(new entries) regardless of how long the collection has grown — the load-bearing property for long-running workflows whose History/ArtifactHistory grow without bound.

Optional. Callers (typically the SDK workflow code) type-assert; the caller surfaces a clear error when the store doesn't satisfy.

type ListOptions

type ListOptions struct {
	// UserID filters conversations by the user who owns them.
	// If empty, all conversations are returned (subject to pagination).
	UserID string

	// Limit is the maximum number of conversation IDs to return.
	// If 0, a default limit (e.g., 100) should be applied.
	Limit int

	// Offset is the number of conversations to skip (for pagination).
	Offset int

	// SortBy specifies the field to sort by (e.g., "created_at", "updated_at").
	// If empty, implementation-specific default sorting is used.
	SortBy string

	// SortOrder specifies sort direction: "asc" or "desc".
	// If empty, defaults to "desc" (newest first).
	SortOrder string
}

ListOptions provides filtering and pagination options for listing conversations.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore provides an in-memory implementation of the Store interface. It is thread-safe and suitable for development, testing, and single-instance deployments. For distributed systems, use RedisStore or a database-backed implementation.

func NewMemoryStore

func NewMemoryStore(opts ...MemoryStoreOption) *MemoryStore

NewMemoryStore creates a new in-memory state store. By default, DefaultTTL and DefaultMaxEntries are applied to prevent unbounded memory growth in server scenarios. Options can be provided to override these defaults. Use WithNoTTL() and/or WithNoMaxEntries() to explicitly disable limits.

func (*MemoryStore) AppendList added in v1.4.7

func (s *MemoryStore) AppendList(ctx context.Context, id, listName string, items [][]byte) error

AppendList appends items to the named list for the conversation. Auto-creates the conversation entry if missing. Items are deep-copied so subsequent caller mutations don't affect stored state.

func (*MemoryStore) AppendMessages added in v1.3.1

func (s *MemoryStore) AppendMessages(ctx context.Context, id string, messages []types.Message) error

AppendMessages appends messages to the conversation's message history. If the entry is expired, it is treated as non-existent and a new state is created.

func (*MemoryStore) Close added in v1.3.2

func (s *MemoryStore) Close()

Close stops the background eviction goroutine, if running. It is safe to call Close multiple times.

func (*MemoryStore) Delete

func (s *MemoryStore) Delete(ctx context.Context, id string) error

Delete removes a conversation state by ID.

func (*MemoryStore) Fork added in v1.1.6

func (s *MemoryStore) Fork(ctx context.Context, sourceID, newID string) error

Fork creates a copy of an existing conversation state with a new ID.

func (*MemoryStore) Len added in v1.3.2

func (s *MemoryStore) Len() int

Len returns the number of entries currently in the store, including expired entries that have not yet been evicted. This is primarily useful for testing.

func (*MemoryStore) List

func (s *MemoryStore) List(ctx context.Context, opts ListOptions) ([]string, error)

List returns conversation IDs matching the given criteria. Expired entries are excluded from results.

func (*MemoryStore) ListLen added in v1.4.7

func (s *MemoryStore) ListLen(ctx context.Context, id, listName string) (int, error)

ListLen returns the length of the named list. ErrNotFound only when the conversation itself doesn't exist.

func (*MemoryStore) Load

Load retrieves a conversation state by ID. Returns a deep copy to prevent external mutations. Expired entries are lazily evicted on access and return ErrNotFound.

func (*MemoryStore) LoadList added in v1.4.7

func (s *MemoryStore) LoadList(ctx context.Context, id, listName string) ([][]byte, error)

LoadList returns all items of the named list, deep-copied. Returns (nil, nil) when the list is empty or never written. Returns ErrNotFound when the conversation itself doesn't exist.

func (*MemoryStore) LoadMetadata added in v1.3.10

func (s *MemoryStore) LoadMetadata(ctx context.Context, id string) (map[string]interface{}, error)

LoadMetadata returns just the metadata map for the given conversation. This avoids the cost of deep-copying the entire message history, making it significantly cheaper than Load() for callers that only need metadata. Expired entries are lazily evicted and return ErrNotFound.

func (*MemoryStore) LoadRecentMessages added in v1.3.1

func (s *MemoryStore) LoadRecentMessages(ctx context.Context, id string, n int) ([]types.Message, error)

LoadRecentMessages returns the last n messages for the given conversation. Expired entries return ErrNotFound.

func (*MemoryStore) LoadSummaries added in v1.3.1

func (s *MemoryStore) LoadSummaries(ctx context.Context, id string) ([]Summary, error)

LoadSummaries returns all summaries for the given conversation. Uses a read lock since summaries contain only value types and cloning is a simple slice copy that doesn't benefit from write-lock protection. Expired entries return nil but are not eagerly evicted under RLock.

func (*MemoryStore) LogAppend added in v1.3.24

func (s *MemoryStore) LogAppend(ctx context.Context, id string, startSeq int, messages []types.Message) (int, error)

LogAppend appends messages with sequence-based idempotent deduplication. Messages before startSeq are already persisted and are skipped.

func (*MemoryStore) LogLen added in v1.3.24

func (s *MemoryStore) LogLen(ctx context.Context, id string) (int, error)

LogLen returns the total message count for the conversation. Returns 0 if the conversation doesn't exist.

func (*MemoryStore) LogLoad added in v1.3.24

func (s *MemoryStore) LogLoad(ctx context.Context, id string, recent int) ([]types.Message, error)

LogLoad returns messages for the conversation. If recent > 0, returns only the last N messages. Returns an empty slice if the conversation doesn't exist.

func (*MemoryStore) MergeMetadata added in v1.4.7

func (s *MemoryStore) MergeMetadata(ctx context.Context, id string, updates map[string]interface{}) error

MergeMetadata atomically merges the supplied keys into the conversation's Metadata map. Auto-creates the conversation if it doesn't exist.

func (*MemoryStore) MessageCount added in v1.3.1

func (s *MemoryStore) MessageCount(ctx context.Context, id string) (int, error)

MessageCount returns the total number of messages in the conversation. Uses a read lock since it only needs the message count, not a mutable reference. Expired entries return ErrNotFound but are not eagerly evicted under RLock; they will be cleaned up on the next write operation or background eviction.

func (*MemoryStore) Save

func (s *MemoryStore) Save(ctx context.Context, state *ConversationState) error

Save persists a conversation state. If it already exists, it will be updated. When a max entries limit is set and the store is full, the least-recently-accessed entry is evicted to make room.

func (*MemoryStore) SaveSummary added in v1.3.1

func (s *MemoryStore) SaveSummary(ctx context.Context, id string, summary Summary) error

SaveSummary appends a summary to the conversation's summary list. Auto-creates the conversation if it doesn't exist.

type MemoryStoreOption added in v1.3.2

type MemoryStoreOption func(*MemoryStore)

MemoryStoreOption configures optional behavior for MemoryStore.

func WithMemoryEvictionInterval added in v1.3.2

func WithMemoryEvictionInterval(d time.Duration) MemoryStoreOption

WithMemoryEvictionInterval sets the interval for the background cleanup goroutine that removes expired entries. If zero, no background cleanup runs and eviction happens only lazily on access. Requires a non-zero TTL to have any effect.

func WithMemoryMaxEntries added in v1.3.2

func WithMemoryMaxEntries(n int) MemoryStoreOption

WithMemoryMaxEntries sets the maximum number of entries the store will hold. When the limit is reached, the least-recently-accessed entry is evicted. A zero or negative value disables the limit. By default, DefaultMaxEntries is applied. Use WithNoMaxEntries() as an explicit, self-documenting way to disable the entry limit.

func WithMemoryTTL added in v1.3.2

func WithMemoryTTL(ttl time.Duration) MemoryStoreOption

WithMemoryTTL sets the time-to-live for conversation states. Entries that have not been accessed within the TTL are considered expired and eligible for eviction. A zero or negative TTL disables expiration. By default, DefaultTTL is applied. Use WithNoTTL() as an explicit, self-documenting way to disable TTL expiration.

func WithNoMaxEntries added in v1.3.10

func WithNoMaxEntries() MemoryStoreOption

WithNoMaxEntries explicitly disables the max entries limit so the store can grow unbounded. This overrides the DefaultMaxEntries that is otherwise applied automatically.

func WithNoTTL added in v1.3.10

func WithNoTTL() MemoryStoreOption

WithNoTTL explicitly disables TTL expiration so entries never expire. This overrides the DefaultTTL that is otherwise applied automatically.

type MessageAppender added in v1.3.1

type MessageAppender interface {
	// AppendMessages appends messages to the conversation's message history.
	// Auto-creates the conversation if it doesn't exist.
	AppendMessages(ctx context.Context, id string, messages []types.Message) error
}

MessageAppender allows appending messages without a full load+replace+save cycle. This is an optional interface — stores that implement it enable incremental saves. Pipeline stages type-assert for this interface and fall back to BulkWriter when unavailable.

type MessageIndex added in v1.3.1

type MessageIndex interface {
	// Index adds a message to the search index for the given conversation.
	// turnIndex is the position of the message in the conversation history.
	Index(ctx context.Context, conversationID string, turnIndex int, message types.Message) error

	// Search finds the top-k messages most relevant to the query string.
	// Results are ordered by descending relevance score.
	Search(ctx context.Context, conversationID string, query string, k int) ([]IndexResult, error)

	// Delete removes all indexed messages for a conversation.
	Delete(ctx context.Context, conversationID string) error
}

MessageIndex provides semantic search over conversation messages. Implementations can use embedding-based vector search or other similarity methods to find messages relevant to a given query.

type MessageLog added in v1.3.24

type MessageLog interface {
	// LogAppend appends messages starting at the given sequence number.
	// If startSeq < current message count, the first (current - startSeq) input
	// messages are skipped (idempotent deduplication). Returns the new total count.
	LogAppend(ctx context.Context, id string, startSeq int, messages []types.Message) (int, error)

	// LogLoad returns messages for the conversation.
	// If recent > 0, returns only the last N messages.
	// If recent == 0, returns all messages.
	// Returns an empty slice (not an error) if the conversation doesn't exist.
	LogLoad(ctx context.Context, id string, recent int) ([]types.Message, error)

	// LogLen returns the total message count for the conversation.
	// Returns 0 (not an error) if the conversation doesn't exist.
	LogLen(ctx context.Context, id string) (int, error)
}

MessageLog provides sequence-based message persistence with idempotent append. Stores that implement this interface enable per-round write-through during tool loops, so messages survive process crashes without waiting for the end-of-pipeline save stage.

The sequence number is the message index in the conversation (0-based). AppendMessages with startSeq < current length skips already-persisted messages, making retries safe.

Implementations are not required to be safe against concurrent LogAppend calls targeting the same conversation id. Callers must serialize writes per conversation — the ProviderStage tool loop already does this via per-conversation single-threading.

type MessageReader added in v1.3.1

type MessageReader interface {
	// LoadRecentMessages returns the last n messages for the given conversation.
	// Returns ErrNotFound if the conversation doesn't exist.
	LoadRecentMessages(ctx context.Context, id string, n int) ([]types.Message, error)

	// MessageCount returns the total number of messages in the conversation.
	// Returns ErrNotFound if the conversation doesn't exist.
	MessageCount(ctx context.Context, id string) (int, error)
}

MessageReader allows loading a subset of messages without full state deserialization. This is an optional interface — stores that implement it enable efficient partial reads. Pipeline stages type-assert for this interface and fall back to Store.Load when unavailable.

type MetadataAccessor added in v1.3.10

type MetadataAccessor interface {
	// LoadMetadata returns just the metadata map for the given conversation.
	// Returns ErrNotFound if the conversation doesn't exist.
	// The returned map is a deep copy safe for mutation by the caller.
	LoadMetadata(ctx context.Context, id string) (map[string]interface{}, error)

	// MergeMetadata atomically merges the supplied keys into the
	// conversation's Metadata map. Existing keys are overwritten, other
	// fields (Messages, Summaries, etc.) are untouched. Auto-creates the
	// conversation if it doesn't exist.
	MergeMetadata(ctx context.Context, id string, updates map[string]interface{}) error
}

MetadataAccessor allows reading and writing metadata without loading the full state. This is an optional interface — stores that implement it enable efficient metadata operations without the cost of deep-copying the message history. Pipeline stages type-assert for this interface; reads fall back to Store.Load when LoadMetadata is unavailable, and writes fall back to BulkWriter when MergeMetadata is unavailable.

type RedisOption

type RedisOption func(*RedisStore)

RedisOption configures a RedisStore.

func WithPrefix

func WithPrefix(prefix string) RedisOption

WithPrefix sets the key prefix for Redis keys. Default is "promptkit".

func WithTTL

func WithTTL(ttl time.Duration) RedisOption

WithTTL sets the time-to-live for conversation states. After this duration, conversations will be automatically deleted. Default is 24 hours. Set to 0 for no expiration.

type RedisStore

type RedisStore struct {
	// contains filtered or unexported fields
}

RedisStore provides a Redis-backed implementation of the Store interface. It uses JSON serialization for state storage and supports automatic TTL-based cleanup. This implementation is suitable for distributed systems and production deployments.

func NewRedisStore

func NewRedisStore(client *redis.Client, opts ...RedisOption) *RedisStore

NewRedisStore creates a new Redis-backed state store.

Example:

store := NewRedisStore(
    redis.NewClient(&redis.Options{Addr: "localhost:6379"}),
    WithTTL(24 * time.Hour),
    WithPrefix("myapp"),
)

func (*RedisStore) AppendList added in v1.4.7

func (s *RedisStore) AppendList(ctx context.Context, id, listName string, items [][]byte) error

AppendList appends opaque items to the named per-conversation list using RPUSH. The list name is also recorded in a per-conversation index set (SADD) so Delete can enumerate list keys without a SCAN. All commands run in a single pipeline; TTLs are refreshed on the list, the index set, and the meta key so that long-running workflows that only append list entries don't expire prematurely.

Auto-creates the conversation if it doesn't exist — meta is touched on the next interaction (or by an explicit Save). No Save is required.

func (*RedisStore) AppendMessages added in v1.3.1

func (s *RedisStore) AppendMessages(ctx context.Context, id string, messages []types.Message) error

AppendMessages appends messages to the conversation's message list using RPUSH. Uses Redis pipelining to batch the RPUSH, EXPIRE, and meta update in a single round-trip.

func (*RedisStore) Delete

func (s *RedisStore) Delete(ctx context.Context, id string) error

Delete removes a conversation state from Redis. Removes both decomposed keys (meta, messages, summaries) and legacy monolithic key. Uses a pipeline to batch all DEL commands and optional user index cleanup.

func (*RedisStore) Fork added in v1.1.6

func (s *RedisStore) Fork(ctx context.Context, sourceID, newID string) error

Fork creates a copy of an existing conversation state with a new ID.

func (*RedisStore) List

func (s *RedisStore) List(ctx context.Context, opts ListOptions) ([]string, error)

List returns conversation IDs matching the given criteria.

func (*RedisStore) ListLen added in v1.4.7

func (s *RedisStore) ListLen(ctx context.Context, id, listName string) (int, error)

ListLen returns the current length of the named list. Returns 0 for empty/missing lists; ErrNotFound only when the conversation itself doesn't exist.

func (*RedisStore) Load

func (s *RedisStore) Load(ctx context.Context, id string) (*ConversationState, error)

Load retrieves a conversation state by ID from Redis. Tries the decomposed format (meta key + messages list + summaries list) first, then falls back to the legacy monolithic JSON string for backward compatibility.

func (*RedisStore) LoadList added in v1.4.7

func (s *RedisStore) LoadList(ctx context.Context, id, listName string) ([][]byte, error)

LoadList returns all items of the named list in append order. Returns (nil, nil) when the list is empty or has never been written and the conversation exists. Returns ErrNotFound only when the conversation itself doesn't exist (distinguished by an EXISTS check on the meta key).

func (*RedisStore) LoadMetadata added in v1.3.10

func (s *RedisStore) LoadMetadata(ctx context.Context, id string) (map[string]interface{}, error)

LoadMetadata returns the user metadata map for the given conversation by reading the dedicated metadata hash. Returns ErrNotFound when the conversation doesn't exist (matches MemoryStore.LoadMetadata). Returns an empty (or nil) map when the conversation exists but has no metadata.

func (*RedisStore) LoadRecentMessages added in v1.3.1

func (s *RedisStore) LoadRecentMessages(ctx context.Context, id string, n int) ([]types.Message, error)

LoadRecentMessages returns the last n messages using LRANGE on the messages list. Falls back to loading from the monolithic key if the list doesn't exist.

func (*RedisStore) LoadSummaries added in v1.3.1

func (s *RedisStore) LoadSummaries(ctx context.Context, id string) ([]Summary, error)

LoadSummaries returns all summaries for the conversation.

func (*RedisStore) LogAppend added in v1.4.12

func (s *RedisStore) LogAppend(
	ctx context.Context, id string, startSeq int, messages []types.Message,
) (int, error)

LogAppend appends messages with sequence-based idempotent deduplication. The on-disk layout matches what Save writes (a Redis list at messagesKey(id)) so messages persisted via the tool-loop write-through path are loaded back transparently by Load. A minimal meta stub is written via SETNX so a subsequent Load(id) does not return ErrNotFound after a crash recovery before the end-of-turn Save has run; the next Save overwrites it.

func (*RedisStore) LogLen added in v1.4.12

func (s *RedisStore) LogLen(ctx context.Context, id string) (int, error)

LogLen returns the message count for the conversation, or 0 if absent.

func (*RedisStore) LogLoad added in v1.4.12

func (s *RedisStore) LogLoad(ctx context.Context, id string, recent int) ([]types.Message, error)

LogLoad returns messages for the conversation. Empty (not an error) when the conversation doesn't exist. With recent > 0, returns only the last N.

func (*RedisStore) MergeMetadata added in v1.4.7

func (s *RedisStore) MergeMetadata(ctx context.Context, id string, updates map[string]interface{}) error

MergeMetadata writes the supplied keys into the conversation's metadata hash via a single HMSET. Each Redis hash field write is server-atomic, so concurrent MergeMetadata calls on the same conversation just work — no WATCH/MULTI/EXEC, no retry loop. Updates the meta-key TTL too so metadata-only writes count as activity for retention purposes.

func (*RedisStore) MessageCount added in v1.3.1

func (s *RedisStore) MessageCount(ctx context.Context, id string) (int, error)

MessageCount returns the total number of messages. Falls back to loading from the monolithic key if the list doesn't exist.

func (*RedisStore) Save

func (s *RedisStore) Save(ctx context.Context, state *ConversationState) error

Save persists a conversation state to Redis using decomposed keys. Metadata is stored in a meta key, messages in a Redis list, and summaries in a separate list. Messages use append-only delta writes: only new messages (beyond what is already stored) are RPUSHed, avoiding the cost of DEL+RPUSH for the entire list on every save. If the message count in Redis exceeds the local count (e.g., external truncation), a full rewrite is performed.

func (*RedisStore) SaveSummary added in v1.3.1

func (s *RedisStore) SaveSummary(ctx context.Context, id string, summary Summary) error

SaveSummary appends a summary to the conversation's summary list. Uses a pipeline to batch RPUSH and EXPIRE into a single round-trip.

type Store

type Store interface {
	// Load retrieves conversation state by ID
	Load(ctx context.Context, id string) (*ConversationState, error)

	// Fork creates a copy of an existing conversation state with a new ID
	// The original conversation is left unchanged. Returns ErrNotFound if sourceID doesn't exist.
	Fork(ctx context.Context, sourceID, newID string) error
}

Store defines the interface for persistent conversation state storage.

Store is read-shaped: it deliberately does not include bulk-write methods. Callers needing to persist state should use the typed write interfaces (MessageAppender, MetadataAccessor.MergeMetadata, SummaryAccessor.SaveSummary). Admin/seed paths that need to replace whole state should type-assert for BulkWriter; hot-path pipeline stages must not.

type Summarizer added in v1.3.1

type Summarizer interface {
	// Summarize compresses the given messages into a concise summary.
	Summarize(ctx context.Context, messages []types.Message) (string, error)
}

Summarizer compresses a batch of messages into a summary string. Implementations may use LLM providers, extractive methods, or other compression strategies.

type Summary

type Summary struct {
	StartTurn  int       // First turn included in this summary
	EndTurn    int       // Last turn included in this summary
	Content    string    // Summarized content
	TokenCount int       // Token count of the summary
	CreatedAt  time.Time // When this summary was created
}

Summary represents a compressed version of conversation turns. Used to maintain context while reducing token count for older conversations.

type SummaryAccessor added in v1.3.1

type SummaryAccessor interface {
	// LoadSummaries returns all summaries for the given conversation.
	// Returns nil (not an error) if no summaries exist.
	LoadSummaries(ctx context.Context, id string) ([]Summary, error)

	// SaveSummary appends a summary to the conversation's summary list.
	// Auto-creates the conversation if it doesn't exist.
	SaveSummary(ctx context.Context, id string, summary Summary) error
}

SummaryAccessor allows reading and writing summaries independently of the full state. This is an optional interface for stores that support efficient summary operations.

Directories

Path Synopsis
Package file provides a filesystem-backed statestore implementation.
Package file provides a filesystem-backed statestore implementation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL