session

package
v0.2.75 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package session gives a conversation a durable identity, metadata and scoped state.

Scoping in the SDK is ambient: memory derives an "orgID:conversationID" key from the context and hard-fails if either is missing. That is enough to isolate transcripts and no more. There is no object describing a conversation, nothing to list with titles and timestamps, nowhere to keep a fact that should outlive one turn, and no way to resume by ID with anything other than a bare transcript.

A Session supplies that. It deliberately does NOT own the transcript: pkg/memory already stores messages, keyed by the same conversation ID a Session is identified by. Duplicating messages here would create two sources of truth for the same data.

Session.ID == the conversation ID used by pkg/memory

State scoping

State keys are scoped by prefix, following the model Google's ADK uses:

"user:"   shared across every session belonging to one user
"app:"    shared across every session in the application
"temp:"   never persisted; dropped on save
(none)    private to this session

The prefixes are resolved at the storage layer, so a caller reads and writes one flat namespace.

Index

Constants

View Source
const (
	// UserPrefix scopes a key to the user, across all their sessions.
	UserPrefix = "user:"
	// AppPrefix scopes a key to the application, across all users.
	AppPrefix = "app:"
	// TempPrefix marks a key as never persisted.
	TempPrefix = "temp:"
)

State key prefixes.

Variables

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

ErrNotFound is returned when no session matches an ID.

Functions

func DeriveTitle

func DeriveTitle(message string) string

DeriveTitle produces a short label from a message.

Kept deliberately dumb -- truncation on a word boundary, no model call. A title is a listing convenience, and spending a model round trip (and its latency, cost and failure mode) on one is not a good trade.

func NewID

func NewID() string

NewID mints a session ID.

Types

type Dialect

type Dialect string

Dialect names a SQL flavour.

const (
	// Postgres uses $N placeholders and ON CONFLICT upserts.
	Postgres Dialect = "postgres"
	// SQLite uses ? placeholders and ON CONFLICT upserts.
	SQLite Dialect = "sqlite"
)

type Filter

type Filter struct {
	OrgID  string
	UserID string

	// Limit caps the number of sessions returned. Zero means no limit.
	Limit int
}

Filter narrows a session listing.

type InMemoryStore

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

InMemoryStore keeps sessions in process. Suitable for tests, single-process deployments and local development.

func NewInMemoryStore

func NewInMemoryStore() *InMemoryStore

NewInMemoryStore creates an in-process session store.

func (*InMemoryStore) Create

func (s *InMemoryStore) Create(_ context.Context, sess *Session) error

Create implements Store.

func (*InMemoryStore) Delete

func (s *InMemoryStore) Delete(_ context.Context, orgID, id string) error

Delete implements Store.

func (*InMemoryStore) Get

func (s *InMemoryStore) Get(_ context.Context, orgID, id string) (*Session, error)

Get implements Store.

func (*InMemoryStore) List

func (s *InMemoryStore) List(_ context.Context, filter Filter) ([]*Session, error)

List implements Store.

func (*InMemoryStore) Update

func (s *InMemoryStore) Update(_ context.Context, sess *Session) error

Update implements Store.

type Manager

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

Manager creates and resolves sessions, and stamps the identity that memory scoping requires onto a context.

This is the piece that makes sessions useful rather than decorative. Memory derives its scope key from multitenancy.GetOrgID and memory.GetConversationID and hard-fails when either is missing -- which is why a scheduled or background run, having no inbound request to inherit from, could not write to memory at all. Resolve produces a context carrying both.

func NewManager

func NewManager(store Store, options ...ManagerOption) *Manager

NewManager creates a session manager over a store.

func (*Manager) Bind

func (m *Manager) Bind(ctx context.Context, s *Session) context.Context

Bind stamps a session's identity onto a context so memory can scope to it.

Call this before handing the context to an agent. Without it an agent with memory configured fails its first turn, because getConversationID requires both an org ID and a conversation ID.

func (*Manager) Create

func (m *Manager) Create(ctx context.Context, orgID, userID string) (*Session, error)

Create starts a new session.

func (*Manager) Delete

func (m *Manager) Delete(ctx context.Context, orgID, id string) error

Delete removes a session.

func (*Manager) Fork

func (m *Manager) Fork(ctx context.Context, orgID, id string) (*Session, error)

Fork creates a new session seeded with another's state.

The transcript is not copied: sessions reference messages by conversation ID rather than owning them, so a fork starts a fresh conversation carrying the parent's accumulated state. The parent is recorded under "forked_from".

func (*Manager) Get

func (m *Manager) Get(ctx context.Context, orgID, id string) (*Session, error)

Get returns an existing session.

func (*Manager) List

func (m *Manager) List(ctx context.Context, filter Filter) ([]*Session, error)

List returns sessions matching the filter.

func (*Manager) Resume

func (m *Manager) Resume(ctx context.Context, orgID, id string) (*Session, context.Context, error)

Resume returns an existing session and a context scoped to it, ready to hand to an agent.

func (*Manager) Save

func (m *Manager) Save(ctx context.Context, s *Session) error

Save persists changes to a session.

func (*Manager) Touch

func (m *Manager) Touch(ctx context.Context, s *Session, messages, tokens int, firstMessage string) error

Touch records activity on a session: it refreshes UpdatedAt, adds to the message and token counters, and derives a title from the first message if the session does not have one yet.

type ManagerOption

type ManagerOption func(*Manager)

ManagerOption configures a Manager.

func WithAppName

func WithAppName(name string) ManagerOption

WithAppName sets the application name used to scope "app:" state.

type SQLStore

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

SQLStore persists sessions in any database/sql backend.

It targets Postgres and SQLite, which differ in placeholder syntax ($1 versus ?) and in upsert spelling. Those are the only dialect-specific details, so rather than a driver interface per backend the store carries a small Dialect -- the same tradeoff ADK's DatabaseSessionService makes with its dialect-conditional branches.

State is stored in three tables so the "user:" and "app:" prefixes are genuinely shared rather than copied into every session row. "temp:" keys are dropped before any write.

func NewSQLStore

func NewSQLStore(db *sql.DB, dialect Dialect) (*SQLStore, error)

NewSQLStore creates a session store over an existing *sql.DB.

It takes a live handle rather than a DSN on purpose: a service that already has a pool -- pkg/datastore/postgres, for instance -- should not open a second one just for sessions.

func (*SQLStore) Create

func (s *SQLStore) Create(ctx context.Context, sess *Session) error

Create implements Store.

func (*SQLStore) Delete

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

Delete implements Store.

func (*SQLStore) Get

func (s *SQLStore) Get(ctx context.Context, orgID, id string) (*Session, error)

Get implements Store.

func (*SQLStore) List

func (s *SQLStore) List(ctx context.Context, filter Filter) ([]*Session, error)

List implements Store.

func (*SQLStore) Migrate

func (s *SQLStore) Migrate(ctx context.Context) error

Migrate creates the tables this store needs, if they do not exist.

func (*SQLStore) Update

func (s *SQLStore) Update(ctx context.Context, sess *Session) error

Update implements Store.

type Session

type Session struct {
	// ID is the conversation ID. It is the same value pkg/memory scopes the
	// transcript by, so a session and its messages are never out of step.
	ID string

	// OrgID is the owning organization, matching pkg/multitenancy.
	OrgID string

	// UserID identifies the end user, and scopes "user:" state.
	UserID string

	// AppName scopes "app:" state. Optional.
	AppName string

	// Title is a human-readable label, usually derived from the first message.
	Title string

	// State is the session's scoped key-value store. Read it through Get and
	// write it through Set so prefixes are handled consistently.
	State map[string]any

	CreatedAt time.Time
	UpdatedAt time.Time

	// MessageCount and TokenTotal are denormalised counters, maintained by the
	// caller. They exist so a session list can show useful numbers without
	// loading every transcript.
	MessageCount int
	TokenTotal   int
}

Session is one conversation.

func (*Session) Clone

func (s *Session) Clone() *Session

Clone returns a deep copy, so a caller cannot mutate a store's internals through a returned session.

func (*Session) Delete

func (s *Session) Delete(key string)

Delete removes a state key.

func (*Session) Get

func (s *Session) Get(key string) (any, bool)

Get reads a state key.

func (*Session) GetString

func (s *Session) GetString(key string) string

GetString reads a state key as a string.

func (*Session) Set

func (s *Session) Set(key string, value any)

Set writes a state key. Prefixes determine how far the value is shared; see the package documentation.

type Store

type Store interface {
	// Create stores a new session. It returns an error if the ID already exists.
	Create(ctx context.Context, s *Session) error

	// Get returns a session by ID, with shared state merged in.
	Get(ctx context.Context, orgID, id string) (*Session, error)

	// Update replaces a session's mutable fields.
	Update(ctx context.Context, s *Session) error

	// Delete removes a session and its private state.
	Delete(ctx context.Context, orgID, id string) error

	// List returns sessions matching the filter, most recently updated first.
	// The returned sessions carry metadata but not state.
	List(ctx context.Context, filter Filter) ([]*Session, error)
}

Store persists sessions.

Implementations must treat state prefixes as described in the package documentation: "temp:" keys are dropped on save, and "user:"/"app:" keys are shared across the sessions in that scope rather than copied into each.

Jump to

Keyboard shortcuts

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