memory

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2025 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ConversationIDKey = conversationID("conversationID")
View Source
var MessageIDKey = messageID("messageID")

Functions

func ConversationIDFromContext

func ConversationIDFromContext(ctx context.Context) string

func MessageIDFromContext

func MessageIDFromContext(ctx context.Context) string

func NewChildConversation

func NewChildConversation(ctx context.Context, hist History, parentID, title, visibility string) string

NewChildConversation creates a conversation ID, stores meta and returns it. It is safe to call with nil History.

Types

type Attachment

type Attachment struct {
	Name string `json:"name,omitempty" yaml:"name"`
	URL  string `json:"url,omitempty"  yaml:"url"`
	Size int64  `json:"size,omitempty" yaml:"size"` // bytes
	// MediaType allows UI to decide how to display or download.
	MediaType string `json:"mediaType,omitempty" yaml:"mediaType,omitempty"`
}

Attachment describes a file linked to the message.

type Attachments

type Attachments []Attachment

type CombinedPolicy

type CombinedPolicy struct {
	Policies []Policy
}

CombinedPolicy composes multiple policies in order.

func NewCombinedPolicy

func NewCombinedPolicy(policies ...Policy) *CombinedPolicy

NewCombinedPolicy creates a policy that applies all given policies sequentially.

func (*CombinedPolicy) Apply

func (c *CombinedPolicy) Apply(ctx context.Context, messages []Message) ([]Message, error)

Apply runs each policy in sequence.

type ConversationMeta

type ConversationMeta struct {
	ID         string    `json:"id"`
	ParentID   string    `json:"parentId,omitempty"`
	Title      string    `json:"title,omitempty"`
	Visibility string    `json:"visibility,omitempty"` // full|summary|none
	CreatedAt  time.Time `json:"createdAt"`

	// Model stores the last LLM model explicitly chosen by the user within
	// this conversation. When a subsequent turn omits the model override the
	// orchestration can fall back to this value so that the user does not
	// have to repeat the flag every time.
	Model string `json:"model,omitempty"`

	// Tools keeps the last explicit per-turn tool allow-list requested by the
	// user. When a subsequent turn sends an empty tools slice, orchestration
	// falls back to this stored list so the preference persists.
	Tools []string `json:"tools,omitempty"`

	// Agent records the last agent configuration reference (path or name)
	// explicitly used in the conversation so that subsequent requests can
	// omit the field and still continue the thread with the same agent.
	Agent string `json:"agent,omitempty"`

	// Context holds the latest accepted elicitation payload so that the user
	// does not have to resend the same data every turn when the same agent
	// schema still applies.
	Context map[string]interface{} `json:"context,omitempty"`
}

ConversationMeta captures hierarchical metadata for a conversation. It is kept minimal so that additional fields can be added without breaking existing callers.

type EmbedFunc

type EmbedFunc func(ctx context.Context, texts []string) ([][]float32, error)

EmbedFunc defines a function that creates embeddings for given texts. It should return one embedding per input text.

type ExecutionStore

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

ExecutionStore is an in-memory registry of execution traces keyed by conversation ID.

func NewExecutionStore

func NewExecutionStore() *ExecutionStore

NewExecutionStore returns an empty store.

func (*ExecutionStore) Add

func (t *ExecutionStore) Add(ctx context.Context, convID string, trace *ExecutionTrace) (int, error)

Add appends a trace and assigns a sequential ID.

func (*ExecutionStore) Get

func (t *ExecutionStore) Get(ctx context.Context, convID string, traceID int) (*ExecutionTrace, error)

Get returns a single execution trace by conversation ID and trace ID (1-based). A nil pointer is returned when not found.

func (*ExecutionStore) List

func (t *ExecutionStore) List(ctx context.Context, convID string) ([]*ExecutionTrace, error)

List returns a copy of all traces for conversation.

func (*ExecutionStore) ListByParent

func (t *ExecutionStore) ListByParent(ctx context.Context, convID string, parentMsgID string) ([]*ExecutionTrace, error)

ListByParent returns a subset of traces for the supplied conversation filtered by the ParentMsgID. A nil slice is returned when the conversation ID is unknown or no trace matches the filter. The returned slice is a shallow copy and can be modified by the caller without affecting the store.

func (*ExecutionStore) ListOutcome

func (t *ExecutionStore) ListOutcome(ctx context.Context, convID string, parentID string) ([]*plan.Outcome, error)

ListOutcome groups traces by PlanID and converts them into plan.Outcome

func (*ExecutionStore) Update

func (t *ExecutionStore) Update(ctx context.Context, convID string, traceID int, fn func(*ExecutionTrace)) error

Update applies an in-place mutation to a previously stored trace identified by conversation ID and trace ID. A no-op when the trace cannot be found.

type ExecutionTrace

type ExecutionTrace struct {
	// Auto-incremented per-conversation identifier.
	ID int `json:"id" yaml:"id"`

	// Parent assistant message ID that triggered this tool call.
	ParentMsgID string `json:"parentId" yaml:"parentId"`

	// Canonical service method, e.g. "file.count".
	Name string `json:"name" yaml:"name"`

	// Marshalled JSON argument object supplied to the tool.
	Request json.RawMessage `json:"request" yaml:"request"`

	// Whether the call succeeded.
	Success bool `json:"success" yaml:"success"`

	// Result (when Success==true) or nil.
	Result json.RawMessage `json:"result,omitempty" yaml:"result,omitempty"`

	// Error message when Success==false.
	Error string `json:"error,omitempty" yaml:"error,omitempty"`

	StartedAt time.Time `json:"startedAt" yaml:"startedAt"`
	EndedAt   time.Time `json:"endedAt" yaml:"endedAt"`

	// Optional plan context – allows UI to tie tool invocation back to the
	// generating plan step.
	PlanID    string     `json:"planId,omitempty" yaml:"planId,omitempty"`
	StepIndex int        `json:"stepIndex,omitempty" yaml:"stepIndex,omitempty"`
	Step      *plan.Step `json:"step,omitempty" yaml:"step,omitempty"`
	Plan      *plan.Plan `json:"plan,omitempty" yaml:"plan,omitempty"`
	// When the step requested additional user input, surface the elicitation so
	// that callers can render an appropriate prompt.
	Elicitation *plan.Elicitation `json:"elicitation,omitempty" yaml:"elicitation,omitempty"`
}

ExecutionTrace captures a single tool invocation (request + result) and optional execution-plan context. It is intended for auditing / UI inspection and is NOT sent back to the LLM as part of the conversation context.

type History

type History interface {
	AddMessage(ctx context.Context, msg Message) error
	GetMessages(ctx context.Context, convID string) ([]Message, error)
	Retrieve(ctx context.Context, convID string, policy Policy) ([]Message, error)

	// UpdateMessage finds message by id within convID and applies mutator.
	UpdateMessage(ctx context.Context, messageId string, mutate func(*Message)) error

	// LookupMessage searches all conversations and returns the first message
	LookupMessage(ctx context.Context, messageID string) (*Message, error)

	LatestMessage(ctx context.Context) (msg *Message, err error)

	// --- Conversation meta management ------------------------------
	CreateMeta(ctx context.Context, id, parentID, title, visibility string)
	Meta(ctx context.Context, id string) (*ConversationMeta, bool)
	Children(ctx context.Context, parentID string) ([]ConversationMeta, bool)
}

History defines behaviour for conversation history storage.

type HistoryStore

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

HistoryStore manages conversation messages by conversation ID.

func NewHistoryStore

func NewHistoryStore() *HistoryStore

NewHistoryStore creates a new in-memory history store.

func (*HistoryStore) AddMessage

func (h *HistoryStore) AddMessage(ctx context.Context, msg Message) error

AddMessage stores a message under the given conversation ID.

func (*HistoryStore) Children

func (h *HistoryStore) Children(ctx context.Context, parentID string) ([]ConversationMeta, bool)

Children returns all direct children for parentID.

func (*HistoryStore) CreateMeta

func (h *HistoryStore) CreateMeta(ctx context.Context, id, parentID, title, visibility string)

CreateMeta registers metadata for a conversation id. If meta already exists it is left unchanged.

func (*HistoryStore) Delete

func (h *HistoryStore) Delete(ctx context.Context, convID string) error

Delete removes every message belonging to the supplied conversation ID.

func (*HistoryStore) EnsureConversation

func (h *HistoryStore) EnsureConversation(convID string)

EnsureConversation makes sure a conversation key exists even if no messages have been added yet. It is safe to call concurrently and will not overwrite existing entries. The method is useful for adapters that need to create a conversation before the first user message arrives (e.g. when the UI offers a "new chat" button).

func (*HistoryStore) GetMessages

func (h *HistoryStore) GetMessages(ctx context.Context, convID string) ([]Message, error)

GetMessages retrieves all messages for the conversation ID.

func (*HistoryStore) LatestMessage

func (h *HistoryStore) LatestMessage(ctx context.Context) (*Message, error)

LatestMessage scans all conversations and returns the latest message encountered. For the simple in-memory store we assume messages are appended in chronological order, therefore the last conversation inspected with a matching message provides the overall latest. While not perfectly accurate in concurrent scenarios it is good enough for local/CLI usage.

func (*HistoryStore) ListIDs

func (h *HistoryStore) ListIDs(ctx context.Context) ([]string, error)

ListIDs returns all conversation IDs currently stored.

func (*HistoryStore) LookupMessage

func (h *HistoryStore) LookupMessage(ctx context.Context, messageID string) (*Message, error)

LookupMessage implements History.LookupMessage by scanning all conversations for the first message whose ID matches messageID. As the in-memory store keeps each conversation in a simple slice, a linear scan is acceptable for local usage. The returned Message is a copy so callers cannot mutate the store inadvertently.

func (*HistoryStore) MessagesSince

func (h *HistoryStore) MessagesSince(ctx context.Context, convID string, sinceID string) ([]Message, error)

MessagesSince returns the slice of messages beginning with the one whose ID matches sinceID (inclusive) followed by every subsequent message in chronological order. If sinceID is empty the full history is returned. When the supplied ID cannot be found the returned slice is empty and no error is raised so that callers can treat it identical to "not yet available".

func (*HistoryStore) Meta

func (h *HistoryStore) Meta(ctx context.Context, id string) (*ConversationMeta, bool)

Meta fetches conversation meta by id.

func (*HistoryStore) Retrieve

func (h *HistoryStore) Retrieve(ctx context.Context, convID string, policy Policy) ([]Message, error)

Retrieve returns messages filtered by the provided policy. If policy is nil, all messages are returned.

func (*HistoryStore) UpdateMessage

func (h *HistoryStore) UpdateMessage(ctx context.Context, messageId string, mutate func(*Message)) error

UpdateMessage applies a mutator function to the message with the given ID across all conversations. If the message is not found the call is a no-op and returns nil so that callers do not have to care about races between polling and updates.

func (*HistoryStore) UpdateMeta

func (h *HistoryStore) UpdateMeta(ctx context.Context, id string, mutate func(*ConversationMeta)) bool

UpdateMeta applies a mutator function to the ConversationMeta identified by id. If the entry does not yet exist it will be created first so that the mutator always receives a valid pointer. The method returns true when an entry existed or was created, false when id is empty.

type LastNPolicy

type LastNPolicy struct {
	N int
}

LastNPolicy keeps only the last N messages.

func NewLastNPolicy

func NewLastNPolicy(n int) *LastNPolicy

NewLastNPolicy creates a policy that retains the last N messages.

func (*LastNPolicy) Apply

func (p *LastNPolicy) Apply(_ context.Context, messages []Message) ([]Message, error)

Apply filters messages to the last N entries.

type Message

type Message struct {
	ID             string  `json:"id"`
	ConversationID string  `json:"conversationId"`
	ParentID       string  `json:"parentId,omitempty"`
	Role           string  `json:"role"`
	Actor          string  `json:"actor,omitempty" yaml:"actor,omitempty"`
	Content        string  `json:"content"`
	ToolName       *string `json:"toolName,omitempty"` // Optional tool name, can be nil
	// When messages include file uploads the Attachments slice describes each
	// uploaded asset (or generated/downloadable asset on assistant side).
	Attachments Attachments     `json:"attachments,omitempty" yaml:"attachments,omitempty" sqlx:"-"`
	Executions  []*plan.Outcome `json:"executions,omitempty" yaml:"executions,omitempty" sqlx:"-"`
	CreatedAt   time.Time       `json:"createdAt" yaml:"createdAt"`

	// Elicitation carries a structured schema-driven prompt when the assistant
	// needs additional user input. When non-nil the UI can render an
	// interactive form instead of plain text. It is omitted for all other
	// message kinds.
	Elicitation *plan.Elicitation `json:"elicitation,omitempty" yaml:"elicitation,omitempty"`

	// CallbackURL is set when the message requires a user action through a
	// dedicated REST callback (e.g. MCP elicitation). Empty for normal chat
	// messages.
	CallbackURL string `json:"callbackURL,omitempty" yaml:"callbackURL,omitempty"`

	// Status indicates the resolution state of interactive MCP prompts.
	// "open"   – waiting for user
	// "done"   – accepted and finished
	// "declined" – user declined
	Status string `json:"status,omitempty" yaml:"status,omitempty"`

	// Interaction contains details for an MCP user-interaction request (e.g.
	// "open the following URL and confirm when done"). When non-nil the UI
	// should render an approval card with a link and Accept/Decline buttons.
	Interaction *UserInteraction `json:"interaction,omitempty" yaml:"interaction,omitempty"`

	// PolicyApproval is non-nil when the system requires explicit user
	// approval before executing a potentially sensitive action (e.g. running
	// an external tool).  The UI should show the approval dialog when the
	// message role == "policyapproval" and Status == "open".
	PolicyApproval *PolicyApproval `json:"policyApproval,omitempty" yaml:"policyApproval,omitempty"`
}

Message represents a conversation message for memory storage.

type NextTokenPolicy

type NextTokenPolicy struct {
	Threshold  int
	Keep       int
	Summarizer SummarizerFunc
	Estimator  TokenEstimatorFunc
}

NextTokenPolicy triggers summarization if total estimated tokens exceed Threshold. It keeps the last Keep messages and replaces earlier ones with a summary.

func NewNextTokenPolicy

func NewNextTokenPolicy(threshold, keep int, summarizer SummarizerFunc, estimator TokenEstimatorFunc) *NextTokenPolicy

NewNextTokenPolicy creates a policy that summarizes if tokens > threshold. If estimator is nil, a default estimator (len(Result)/4) is used.

func (*NextTokenPolicy) Apply

func (p *NextTokenPolicy) Apply(ctx context.Context, messages []Message) ([]Message, error)

Apply applies token-based summarization: if total tokens <= Threshold returns messages, else summarizes oldest messages and prepends summary before last Keep messages.

type Policy

type Policy interface {
	// Apply processes messages, potentially invoking summarization, returning filtered messages or an error.
	Apply(ctx context.Context, messages []Message) ([]Message, error)
}

Policy defines a filter over conversation messages. It may transform, filter, or summarize messages and return the set to include.

func OnlySummariesPolicy

func OnlySummariesPolicy() Policy

OnlySummariesPolicy keeps only messages whose Status is "summary".

func SkipSummariesPolicy

func SkipSummariesPolicy() Policy

SkipSummariesPolicy returns a Policy that removes messages flagged with Status=="summary" or "summarized".

type PolicyApproval

type PolicyApproval struct {
	Tool   string                 `json:"tool" yaml:"tool"`                     // tool/function name such as "system.exec"
	Args   map[string]interface{} `json:"args,omitempty" yaml:"args,omitempty"` // flattened argument map
	Reason string                 `json:"reason,omitempty" yaml:"reason,omitempty"`
}

PolicyApproval captures the details of an approval request that needs an explicit Accept/Reject decision by the user.

type RolePolicy

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

RolePolicy includes only messages whose Role matches one of the allowed roles.

func NewRolePolicy

func NewRolePolicy(roles ...string) *RolePolicy

NewRolePolicy creates a policy that retains messages with specified roles.

func (*RolePolicy) Apply

func (p *RolePolicy) Apply(_ context.Context, messages []Message) ([]Message, error)

Apply filters messages by role.

type StageStore

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

StageStore keeps the latest Stage snapshot per conversation in memory. All operations are concurrency-safe.

func NewStageStore

func NewStageStore() *StageStore

NewStageStore returns an empty store.

func (*StageStore) Get

func (s *StageStore) Get(convID string) *stage.Stage

Get returns a copy of the snapshot for convID, or nil.

func (*StageStore) Set

func (s *StageStore) Set(convID string, st *stage.Stage)

Set replaces the snapshot for convID.

type SummarizerFunc

type SummarizerFunc func(ctx context.Context, messages []Message) (Message, error)

SummarizerFunc defines a function that summarizes a slice of messages. It should return a single Message containing the summary.

type SummaryPolicy

type SummaryPolicy struct {
	Threshold  int
	Summarizer SummarizerFunc
}

SummaryPolicy summarizes older messages when count exceeds Threshold. It replaces the earliest messages with a single summary Message.

func NewSummaryPolicy

func NewSummaryPolicy(threshold int, summarizer SummarizerFunc) *SummaryPolicy

NewSummaryPolicy creates a policy that summarizes messages when count > threshold.

func (*SummaryPolicy) Apply

func (p *SummaryPolicy) Apply(ctx context.Context, messages []Message) ([]Message, error)

Apply applies summarization: if message count > Threshold, summarizes the oldest messages and prepends the summary before the remaining Threshold messages.

type TokenEstimatorFunc

type TokenEstimatorFunc func(Message) int

TokenEstimatorFunc estimates token count for a Message.

type UsageStore

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

UsageStore keeps token-usage statistics per conversation entirely in memory. It is a lightweight helper for tests and CLI use-cases where persisting to a database is overkill. All operations are concurrency-safe.

The layout mirrors the relational design:

– aggregated totals on conversation level (for quick summary)
– one-to-many breakdown per model via usage.Aggregator.

The store purposefully exposes only additive updates – callers cannot decrease counts so that race conditions never lead to negative numbers.

func NewUsageStore

func NewUsageStore() *UsageStore

NewUsageStore returns an empty usage store.

func (*UsageStore) Add

func (s *UsageStore) Add(convID, model string, prompt, completion, embed, cached int)

Add is a lower-level helper that directly increments the counters for convID / model. It is mainly intended for tests.

func (*UsageStore) Aggregator

func (s *UsageStore) Aggregator(convID string) *usage.Aggregator

Aggregator returns the live aggregator for convID or nil when the conversation is unknown. The returned instance is concurrency-safe; callers should treat its contents as read-only.

func (*UsageStore) Conversations

func (s *UsageStore) Conversations() []string

Conversations lists the IDs currently present in the store.

func (*UsageStore) OnUsage

func (s *UsageStore) OnUsage(convID, model string, u *llm.Usage)

OnUsage updates the statistics for convID / model by applying the supplied llm.Usage delta (implements the provider/base.UsageListener contract at the store level).

func (*UsageStore) Totals

func (s *UsageStore) Totals(convID string) (prompt, completion, embed, cached int)

Totals returns the rolled-up counts (prompt, completion, embedding) for the given conversation. When convID is unknown, all numbers are zero.

type UserInteraction

type UserInteraction struct {
	URL         string `json:"url" yaml:"url"`
	Description string `json:"description,omitempty" yaml:"description,omitempty"`
}

UserInteraction represents a structured prompt created via the MCP user-interaction feature.

type VisibilityPolicy

type VisibilityPolicy struct {
	Mode       string
	Summarizer SummarizerFunc
}

VisibilityPolicy filters or transforms a message slice according to the conversation visibility setting as defined by ConversationMeta.Visibility.

full    – include all messages unchanged.
none    – hide every message (empty slice).
summary – collapse the entire slice into a single synthetic summary
          message produced by the supplied Summarizer function.

When Mode == "summary" the Summarizer callback must not be nil. All other modes ignore the field.

func NewVisibilityPolicy

func NewVisibilityPolicy(mode string, summarizer SummarizerFunc) *VisibilityPolicy

NewVisibilityPolicy returns a VisibilityPolicy initialised with the supplied mode and summarizer. The mode string is compared case-insensitively. Callers may pass nil summarizer when mode != "summary".

func (*VisibilityPolicy) Apply

func (p *VisibilityPolicy) Apply(ctx context.Context, messages []Message) ([]Message, error)

Apply fulfils the Policy interface. It either returns the original slice, an empty slice, or a single-element slice containing the summary message.

Jump to

Keyboard shortcuts

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