Documentation
¶
Overview ¶
Package session provides the generic, cross-request lifecycle for persisting a conversation history on top of a memory.Memory store.
It encapsulates:
- per-session locking (one logical lock per "<userId>:<id>") with ref-counted cleanup so the lock map never leaks entries;
- the turn lifecycle BeginTurn -> (Condense) -> Window -> CommitAssistant;
- optional non-destructive condensation (anchored summary) driven by a token threshold, reusing an injected Summarizer.
The package is intentionally policy-free: filtering of agent events, SSE wiring and the choice of Summarizer implementation are injected by the caller. In particular the Summarizer interface is structurally compatible with contextopt.Summarizer, so a single contextopt.NewModelSummarizer(...) instance can be shared between the intra-run optimization middleware and this cross-request condenser without creating an import dependency.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Memory is the underlying cross-request store. Required.
Memory memory.Memory `validate:"required" jsonschema:"description=Underlying cross-request memory store"`
// Summarizer, when non-nil, enables condensation: Condense generates an
// anchored summary and appends it to the store. When nil, Condense is a no-op.
Summarizer Summarizer `jsonschema:"description=Summarizer for condensation, nil disables summarization"`
// CondenseThreshold is the token count of the current window at (or above)
// which Condense triggers a summarization. <= 0 disables condensation.
CondenseThreshold int `validate:"gte=0" jsonschema:"description=Token threshold at which Condense triggers summarization, 0 disables"`
// WindowBudget is the default token budget passed to GetWindow when a turn
// requests its window (and the budget used to evaluate condensation). 0 means
// "use the store's own default" (e.g. FileMemoryConfig.MaxWindowTokens).
WindowBudget int `validate:"gte=0" jsonschema:"description=Default token budget for GetWindow, 0 uses store default"`
// TokenCounter estimates the token count of a window to decide when to
// condense. Defaults to memory.DefaultTokenCounter.
//
// To guarantee a turn never pays the LLM summarization cost twice, inject the
// SAME counter here, in the memory store, and in the intra-run optimization
// middleware (see the plan's anti-double-cost invariant).
TokenCounter memory.TokenCounter `jsonschema:"description=Token estimator for condensation decisions, defaults to memory.DefaultTokenCounter"`
}
Config configures a SessionManager.
type SessionManager ¶
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager owns the per-session locking and turn lifecycle on top of a memory.Memory store.
func NewSessionManager ¶
func NewSessionManager(cfg Config) (*SessionManager, error)
NewSessionManager validates cfg and returns a SessionManager.
func (*SessionManager) BeginTurn ¶
BeginTurn acquires the session lock and loads (or creates) the conversation. The user message is NOT persisted yet: it is held on the Turn and only written durably by CommitAssistant. This keeps it visible to Window/Condense during the turn while guaranteeing that an aborted turn (Discard) persists nothing — so a failed run never leaves a dangling user message, and a retry cannot duplicate it.
The returned Turn MUST be released via CommitAssistant or Discard (typically `defer turn.Discard()`), otherwise the session stays locked.
func (*SessionManager) DeleteConversation ¶
func (sm *SessionManager) DeleteConversation(userId, id string) error
DeleteConversation deletes the conversation from the store. It briefly acquires the session lock to avoid racing an in-flight turn; the ref-counted cleanup then removes the now-unused lock entry, preventing a memory leak.
func (*SessionManager) ListConversations ¶
func (sm *SessionManager) ListConversations(userId string) ([]string, error)
ListConversations forwards to the underlying store.
type Summarizer ¶
type Summarizer = summarizer.Summarizer
Summarizer is a type alias for the summarizer.Summarizer interface, enabling pluggable condensation strategies.
type Turn ¶
type Turn struct {
// contains filtered or unexported fields
}
Turn represents a single locked request/response cycle on one conversation.
Lifecycle: BeginTurn -> [Condense] -> Window -> CommitAssistant (or Discard on failure). The handle is single-use and not safe for concurrent use.
func (*Turn) CommitAssistant ¶
CommitAssistant persists the pending user message (from BeginTurn) followed by the assistant message (when non-nil), then releases the session lock. It guards against a double commit; the lock release is idempotent.
func (*Turn) Condense ¶
Condense generates and persists an anchored summary when the current window reaches the configured token threshold. It is a no-op (returns false) when no Summarizer is configured or the threshold is disabled (<= 0) or not reached.
The threshold check includes the pending user message (full window); the summarization itself covers only the persisted history — the pending user is the new, unprocessed turn that the model will see separately. This prevents the model from receiving the current question twice (once in the summary and once explicitly).
The produced summary is appended non-destructively (the full log is preserved); subsequent windows start from this new summary. Summarization reuses the injected Summarizer and the marker shared with contextopt (memory.SummaryMarkerKey), so persisted summaries interoperate natively with the intra-run optimization middleware.
func (*Turn) Conversation ¶
func (t *Turn) Conversation() memory.Conversation
Conversation exposes the underlying conversation for advanced/read-only use. Note: the current turn's user message is pending and not part of the conversation's persisted messages until CommitAssistant.
func (*Turn) Discard ¶
func (t *Turn) Discard()
Discard releases the session lock without persisting the pending user message or any assistant message. It is safe to call multiple times and after CommitAssistant (no-op), making it ideal for `defer turn.Discard()`.
func (*Turn) Window ¶
Window returns the windowed history [last summary + recent messages] to feed to the model, with the current turn's (not-yet-persisted) user message appended. When budget <= 0, the SessionManager's configured WindowBudget is used (which itself may fall back to the store's default).