store

package
v0.0.0-...-5ed4770 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package store provides data access interfaces and implementations.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrConversationNotFound is returned when a conversation does not exist.
	ErrConversationNotFound = errors.New("conversation not found")

	// ErrMessageNotFound is returned when a message does not exist.
	ErrMessageNotFound = errors.New("message not found")

	// ErrUpdateNotFound is returned when an update does not exist.
	ErrUpdateNotFound = errors.New("update not found")

	// ErrConversationMemberNotFound is returned when a conversation member does not exist.
	ErrConversationMemberNotFound = errors.New("conversation member not found")

	// ErrInvalidDialect is returned when an unsupported database dialect is specified.
	ErrInvalidDialect = errors.New("invalid database dialect")

	// ErrToolExecutionNotFound is returned when a tool execution result does not exist.
	ErrToolExecutionNotFound = errors.New("tool execution not found")

	// ErrDuplicateIdempotencyKey is returned when a message with the same idempotency key already exists.
	ErrDuplicateIdempotencyKey = errors.New("duplicate idempotency key")
)

Sentinel errors for store operations.

These errors are used with fmt.Errorf("%w", ErrXxx) to preserve the error chain. Callers use errors.Is(err, ErrConversationNotFound) to check error types.

Functions

This section is empty.

Types

type Conversation

type Conversation struct {
	ID                     string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	Owner                  string    `gorm:"index;type:varchar(255)" json:"owner"`
	Title                  string    `gorm:"type:varchar(255);default:''" json:"title"`
	LastProcessedMessageID uint64    `gorm:"column:last_processed_message_id;default:0" json:"last_processed_message_id"`
	CreatedAt              time.Time `json:"created_at"`
	UpdatedAt              time.Time `json:"updated_at"`
	LastMessageAt          time.Time `gorm:"column:last_message_at;type:TIMESTAMP;not null;default:CURRENT_TIMESTAMP" json:"last_message_at"`
}

Conversation represents a logical conversation thread with message history and checkpoint data for interruption recovery.

func (Conversation) TableName

func (Conversation) TableName() string

TableName returns the GORM table name for Conversation.

type ConversationCheckpoint

type ConversationCheckpoint struct {
	ConversationID    string    `gorm:"column:conversation_id;primaryKey;type:varchar(36)" json:"conversation_id"`
	Data              []byte    `gorm:"column:data" json:"-"`
	Version           uint64    `gorm:"column:version;default:1" json:"version"`
	SkippedMessageIDs string    `gorm:"column:skipped_message_ids;type:text" json:"-"`
	UpdatedAt         time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}

ConversationCheckpoint stores Agent checkpoint data for interruption recovery. Unlike Conversation, checkpoints are not soft-deleted and are permanently retained.

func (ConversationCheckpoint) TableName

func (ConversationCheckpoint) TableName() string

TableName returns the GORM table name for ConversationCheckpoint.

type ConversationMember

type ConversationMember struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;index:idx_member_conv_user,unique;type:varchar(36)" json:"conversation_id"`
	UserID         string    `gorm:"column:user_id;index:idx_member_conv_user,unique;type:varchar(255)" json:"user_id"`
	ReadMsgID      uint64    `gorm:"column:read_msg_id;default:0" json:"read_msg_id"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

ConversationMember tracks the read watermark (last read message ID) for a user within a conversation.

func (ConversationMember) TableName

func (ConversationMember) TableName() string

TableName returns the GORM table name for ConversationMember.

type ConversationWithMeta

type ConversationWithMeta struct {
	ID            string
	Title         string
	CreatedAt     time.Time
	LastMessageAt time.Time
	ReadMsgID     uint64
	LastMessageID uint64
}

ConversationWithMeta is a read-only projection for list_conversations, combining conversation fields with member read watermark.

type IntentStat

type IntentStat struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;type:varchar(36)" json:"conversation_id"`
	Status         string    `gorm:"type:varchar(20)" json:"status"`
	Duration       int       `json:"duration"`
	IterationCount int       `json:"iteration_count"`
	ToolCount      int       `json:"tool_count"`
	ErrorMessage   string    `gorm:"type:text" json:"error_message"`
	CreatedAt      time.Time `json:"created_at"`
}

IntentStat records execution statistics for an intent processing run.

func (IntentStat) TableName

func (IntentStat) TableName() string

TableName returns the GORM table name for IntentStat.

type LLMStat

type LLMStat struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;type:varchar(36)" json:"conversation_id"`
	MessageID      string    `gorm:"column:message_id;type:varchar(36)" json:"message_id"`
	Model          string    `gorm:"type:varchar(100)" json:"model"`
	InputTokens    int       `json:"input_tokens"`
	OutputTokens   int       `json:"output_tokens"`
	Duration       int       `json:"duration"`
	Status         string    `gorm:"type:varchar(20)" json:"status"`
	ErrorMessage   string    `gorm:"type:text" json:"error_message"`
	CreatedAt      time.Time `json:"created_at"`
}

LLMStat records latency and error statistics for an LLM call.

func (LLMStat) TableName

func (LLMStat) TableName() string

TableName returns the GORM table name for LLMStat.

type Message

type Message struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	MessageID      uint64    `gorm:"column:message_id;index:idx_msg_conv,unique" json:"message_id"`
	ConversationID string    `gorm:"column:conversation_id;index:idx_msg_conv,unique;type:varchar(36)" json:"conversation_id"`
	Role           string    `gorm:"type:varchar(20)" json:"role"`
	Content        string    `gorm:"type:text" json:"content"`
	Compressed     bool      `gorm:"default:false" json:"compressed"`
	IdempotencyKey string    `gorm:"column:idempotency_key;type:varchar(36);default:''" json:"idempotency_key"`
	CreatedAt      time.Time `json:"created_at"`
}

Message represents a single message within a conversation.

func (Message) TableName

func (Message) TableName() string

TableName returns the GORM table name for Message.

type ScopePair

type ScopePair struct {
	Scope   string
	ScopeID string `gorm:"column:scope_id"`
}

ScopePair represents a distinct (scope, scope_id) combination from the updates table.

type StatisticsStore

type StatisticsStore interface {
	// RecordTokenUsage persists token consumption for an LLM call.
	RecordTokenUsage(ctx context.Context, stat *TokenStat) error

	// RecordIntentStat persists execution statistics for an intent processing run.
	RecordIntentStat(ctx context.Context, stat *IntentStat) error

	// RecordToolStat persists execution statistics for a tool call.
	RecordToolStat(ctx context.Context, stat *ToolStat) error

	// RecordLLMStat persists latency and error statistics for an LLM call.
	RecordLLMStat(ctx context.Context, stat *LLMStat) error
}

StatisticsStore defines the persistence layer for agent usage statistics including token counts, intent counts, tool call counts, and LLM error tracking.

Write failures in statistics must not affect the main business flow. Implementations should log errors and return nil, or return errors that callers can safely ignore.

type Store

type Store interface {

	// CreateConversation persists a new conversation.
	CreateConversation(ctx context.Context, conv *Conversation) error

	// GetConversation retrieves a conversation by its ID.
	GetConversation(ctx context.Context, id string) (*Conversation, error)

	// ListConversations returns a paginated list of conversations for the given owner.
	ListConversations(ctx context.Context, owner string, page, size int) ([]*Conversation, error)

	// ListConversationsByUser returns cursor-paginated conversations for a user,
	// joined with their conversation_members read watermark.
	ListConversationsByUser(ctx context.Context, userID string, cursor string, limit int) ([]*ConversationWithMeta, error)

	// UpdateConversation modifies an existing conversation.
	UpdateConversation(ctx context.Context, conv *Conversation) error

	// UpdateLastProcessedMessageID atomically updates last_processed_message_id
	// only when the new value is strictly greater than the current value.
	// This provides a compare-and-swap (CAS) pattern that prevents race conditions
	// when multiple intent handlers process messages concurrently.
	// Returns nil if the update was applied (new value > current value),
	// or nil with rowsAffected=0 if already processed (skip).
	UpdateLastProcessedMessageID(ctx context.Context, conversationID string, messageID uint64) error

	// DeleteConversation removes a conversation and its associated data.
	DeleteConversation(ctx context.Context, id string) error

	// CreateMessage persists a new message within a conversation.
	CreateMessage(ctx context.Context, msg *Message) error

	// GetMessage retrieves a single message by conversation ID and message sequence number.
	GetMessage(ctx context.Context, conversationID string, messageID uint64) (*Message, error)

	// ListMessages returns messages within the given sequence range [from, to).
	ListMessages(ctx context.Context, conversationID string, from, to uint64) ([]*Message, error)

	// UpdateMessage modifies the content of an existing message.
	UpdateMessage(ctx context.Context, msg *Message) error

	// ListActiveMessages returns uncompressed messages for a conversation,
	// sorted by message_id ASC. Used for loading agent context after compression.
	ListActiveMessages(ctx context.Context, conversationID string, limit int) ([]*Message, error)

	// MarkMessagesCompressed bulk-updates compressed=true for messages <= maxMessageID.
	MarkMessagesCompressed(ctx context.Context, conversationID string, maxMessageID uint64) error

	// GetMessageByIdempotencyKey retrieves a message by its idempotency key.
	// Returns ErrMessageNotFound if no message matches.
	GetMessageByIdempotencyKey(ctx context.Context, key string) (*Message, error)

	// CreateUpdate persists a new update record.
	CreateUpdate(ctx context.Context, update *Update) error

	// ListUpdates returns updates within the given sequence range [fromSeq, toSeq] inclusive.
	ListUpdates(ctx context.Context, scope, scopeID string, fromSeq, toSeq uint64) ([]*Update, error)

	// MaxSeq returns the maximum sequence number for a given scope and scopeID.
	// Returns 0 if no records exist.
	MaxSeq(ctx context.Context, scope, scopeID string) (uint64, error)

	// DistinctScopePairs returns all distinct (scope, scopeID) pairs from the updates table.
	// Used during startup to sync Redis seq counters with DB.
	DistinctScopePairs(ctx context.Context) ([]ScopePair, error)

	// DeleteMessagesAfter removes all messages with messageID > the given ID.
	// Used for editMessage rollback scenarios.
	DeleteMessagesAfter(ctx context.Context, conversationID string, messageID uint64) error

	// CreateConversationMember adds a user to a conversation.
	CreateConversationMember(ctx context.Context, member *ConversationMember) error

	// GetConversationMember retrieves a single member by conversation and user IDs.
	GetConversationMember(ctx context.Context, conversationID, userID string) (*ConversationMember, error)

	// UpdateReadMsgID updates the read watermark (last read message ID) for a member.
	UpdateReadMsgID(ctx context.Context, conversationID, userID string, readMsgID uint64) error

	// ListConversationMembers returns all member user IDs for a conversation.
	// Used by the push fanout: agent replies are published to each member's
	// user channel so every device receives the update.
	ListConversationMembers(ctx context.Context, conversationID string) ([]string, error)

	// ListReadBy returns user IDs who have read at least the given message ID.
	ListReadBy(ctx context.Context, conversationID string, msgID uint64) ([]string, error)

	// Transaction executes fn within a database transaction. All Store methods
	// called on the provided tx Store share the same transaction scope.
	// If fn returns an error, the transaction is rolled back.
	Transaction(ctx context.Context, fn func(tx Store) error) error

	// SetCheckpoint stores checkpoint data for a conversation using optimistic locking.
	// Version is atomically incremented on each update.
	SetCheckpoint(ctx context.Context, conversationID string, data []byte) error

	// GetCheckpoint retrieves checkpoint data for a conversation.
	// Returns (nil, nil) if no checkpoint exists.
	GetCheckpoint(ctx context.Context, conversationID string) ([]byte, error)

	// DeleteCheckpoint removes checkpoint data for a conversation.
	DeleteCheckpoint(ctx context.Context, conversationID string) error

	// AppendSkippedMessageID adds a message ID to the skipped list for a conversation.
	AppendSkippedMessageID(ctx context.Context, conversationID string, messageID int64) error

	// ClearSkippedMessageIDs resets the skipped message ID list for a conversation.
	ClearSkippedMessageIDs(ctx context.Context, conversationID string) error

	// GetSkippedMessageIDs retrieves the skipped message ID list for a conversation.
	GetSkippedMessageIDs(ctx context.Context, conversationID string) ([]int64, error)

	// CreateToolExecutionResult persists a tool execution record.
	CreateToolExecutionResult(ctx context.Context, result *ToolExecutionResult) error

	// GetToolExecutionResult retrieves a tool execution record by ID.
	GetToolExecutionResult(ctx context.Context, id string) (*ToolExecutionResult, error)

	// Ping verifies the database connection is alive.
	Ping(ctx context.Context) error
}

Store defines the persistence layer for conversations, messages, members, checkpoints, and statistics. Implementations must be safe for concurrent use.

The Transaction method provides atomic operations — all Store methods provides atomic operations — all Store methods called within the transaction function share the same underlying database transaction.

type TokenStat

type TokenStat struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;type:varchar(36)" json:"conversation_id"`
	MessageID      string    `gorm:"column:message_id;type:varchar(36)" json:"message_id"`
	Model          string    `gorm:"type:varchar(100)" json:"model"`
	InputTokens    int       `json:"input_tokens"`
	OutputTokens   int       `json:"output_tokens"`
	CreatedAt      time.Time `json:"created_at"`
}

TokenStat records token consumption for an LLM call.

func (TokenStat) TableName

func (TokenStat) TableName() string

TableName returns the GORM table name for TokenStat.

type ToolExecutionResult

type ToolExecutionResult struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;type:varchar(36)" json:"conversation_id"`
	MessageID      int64     `gorm:"column:message_id;not null" json:"message_id"`
	ToolName       string    `gorm:"column:tool_name;type:varchar(255);not null" json:"tool_name"`
	Input          string    `gorm:"type:text" json:"input"`
	Output         string    `gorm:"type:text" json:"output"`
	Status         string    `gorm:"type:varchar(20);not null" json:"status"` // success / error / timeout
	DurationMs     int64     `gorm:"column:duration_ms;not null" json:"duration_ms"`
	CreatedAt      time.Time `json:"created_at"`
}

ToolExecutionResult records a single tool invocation with full input/output. Referenced by tool_result Update via tool_execution_id field.

func (ToolExecutionResult) TableName

func (ToolExecutionResult) TableName() string

TableName returns the GORM table name for ToolExecutionResult.

type ToolStat

type ToolStat struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	ConversationID string    `gorm:"column:conversation_id;type:varchar(36)" json:"conversation_id"`
	ToolName       string    `gorm:"type:varchar(100)" json:"tool_name"`
	Status         string    `gorm:"type:varchar(20)" json:"status"`
	Duration       int       `json:"duration"`
	ErrorMessage   string    `gorm:"type:text" json:"error_message"`
	CreatedAt      time.Time `json:"created_at"`
}

ToolStat records execution statistics for a tool call.

func (ToolStat) TableName

func (ToolStat) TableName() string

TableName returns the GORM table name for ToolStat.

type Update

type Update struct {
	ID        string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	Scope     string    `gorm:"type:varchar(20);index:idx_update_scope,unique" json:"scope"`
	ScopeID   string    `gorm:"column:scope_id;type:varchar(255);index:idx_update_scope,unique" json:"scope_id"`
	Seq       uint64    `gorm:"index:idx_update_scope,unique" json:"seq"`
	Type      string    `gorm:"type:varchar(50)" json:"type"`
	Payload   []byte    `json:"payload"`
	CreatedAt time.Time `json:"created_at"`
}

Update represents an instruction or result delivered via the Update mechanism. Updates are independent of conversations and belong to a Session or User scope.

func (Update) TableName

func (Update) TableName() string

TableName returns the GORM table name for Update.

type WebhookDeadLetter

type WebhookDeadLetter struct {
	ID             string    `gorm:"primaryKey;type:varchar(36)" json:"id"`
	TaskID         string    `gorm:"column:task_id;type:varchar(255)" json:"task_id"`
	Event          string    `gorm:"type:varchar(100)" json:"event"`
	UserID         string    `gorm:"column:user_id;type:varchar(255)" json:"user_id"`
	Payload        []byte    `gorm:"type:text" json:"payload"`
	RetryCount     int       `gorm:"column:retry_count" json:"retry_count"`
	LastError      string    `gorm:"column:last_error;type:text" json:"last_error"`
	AttemptHistory []byte    `gorm:"column:attempt_history;type:text" json:"-"`
	CreatedAt      time.Time `json:"created_at"`
	FailedAt       time.Time `gorm:"column:failed_at" json:"failed_at"`
}

WebhookDeadLetter stores failed webhook delivery attempts for later inspection or replay.

func (WebhookDeadLetter) TableName

func (WebhookDeadLetter) TableName() string

TableName returns the GORM table name for WebhookDeadLetter.

Directories

Path Synopsis
Package gorm provides a GORM-based implementation of the store.Store interface.
Package gorm provides a GORM-based implementation of the store.Store interface.

Jump to

Keyboard shortcuts

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