adapters

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ToolSearchMemory = "search_memory"
	ToolCreateMemory = "create_memory"
	ToolUpdateMemory = "update_memory"
	ToolDeleteMemory = "delete_memory"
)
View Source
const (

	// MaxSourceRefsPerMemory bounds durable provenance growth for one memory.
	// The PostgreSQL upsert enforces the same limit atomically.
	MaxSourceRefsPerMemory = 64

	// MaxSourceRefsPerToolResult bounds source locators exposed for one memory
	// hit after validation and authorization.
	MaxSourceRefsPerToolResult = 8
)
View Source
const DefaultBuiltinProviderID = "__builtin_default__"

DefaultBuiltinProviderID is the virtual provider selected when a bot has no persisted memory_provider_id. Registries materialize it per team.

View Source
const SharedMemoryNamespace = "bot"

SharedMemoryNamespace is the namespace bot-shared memory lives in. Every writer — formation, the management API, and the agent-facing write tools — must scope to it, or the write lands somewhere recall never looks.

Variables

View Source
var ErrMemoryScope = errors.New("memory does not belong to this bot")

ErrMemoryScope deliberately does not reveal whether a foreign ID exists.

Functions

func BotScopeFilters added in v0.21.0

func BotScopeFilters(botID string) map[string]any

BotScopeFilters is the canonical scope for one bot's shared memory.

func BuildProfileMetadata

func BuildProfileMetadata(userID, channelIdentityID, displayName string) map[string]any

func EncodeSourceRef

func EncodeSourceRef(sessionID, messageID string) string

EncodeSourceRef builds a "<sessionID>/<messageID>" source ref. The session part is optional so bare message IDs stay valid refs.

func MemoryContextQueryHash

func MemoryContextQueryHash(query string) string

MemoryContextQueryHash returns a stable compact hash for cache keys.

func MemoryLayers added in v0.21.0

func MemoryLayers() []string

MemoryLayers is the layer vocabulary a memory write may declare. It is derived from the node vocabulary rather than restated, so a tool schema and the store it writes into cannot drift apart.

func MergeMetadata

func MergeMetadata(base map[string]any, extra map[string]any) map[string]any

func MergeSourceRefs

func MergeSourceRefs(existing, extra []string) []string

MergeSourceRefs appends new associations to existing provenance and applies the same canonical validation and bound used at storage boundaries.

func NormalizeMemoryLayer added in v0.21.0

func NormalizeMemoryLayer(raw string) string

NormalizeMemoryLayer accepts only the declared vocabulary. An unknown layer is dropped rather than rejected, so the node falls back to its own default instead of failing a write over a cosmetic field.

func NormalizeSourceRefs

func NormalizeSourceRefs(refs []string) []string

NormalizeSourceRefs validates, de-duplicates, and retains the newest associations up to the durable per-memory limit. Input order is association order; retained output keeps that order.

func ParseScopedSourceRef

func ParseScopedSourceRef(ref string) (sessionID, messageID string, ok bool)

ParseScopedSourceRef accepts only the durable ref shape emitted by the chat persistence path. Bare legacy message IDs cannot be authorized across sessions and malformed multi-segment refs are rejected.

func ParseSourceRef

func ParseSourceRef(ref string) (sessionID, messageID string)

ParseSourceRef splits a source ref into its session and message parts. A ref without a separator is treated as a bare message ID.

func RetainSourceRefs

func RetainSourceRefs(refs []string, limit int) []string

RetainSourceRefs validates before applying the limit, so malformed tail entries cannot crowd out older valid provenance.

func StringFromConfig

func StringFromConfig(config map[string]any, key string) string

StringFromConfig extracts a trimmed string value from a config map.

func TruncateSnippet

func TruncateSnippet(s string, n int) string

TruncateSnippet truncates a string to n runes, appending "..." if truncated.

func ValidateMemoryScope added in v0.21.0

func ValidateMemoryScope(botID string, memoryIDs ...string) error

ValidateMemoryScope checks canonical builtin IDs against an independently authorized bot. An ID identifies an object; it never grants access to its bot. Callers must validate a whole batch before performing any mutations.

Types

type AddRequest

type AddRequest struct {
	Message          string         `json:"message,omitempty"`
	Messages         []Message      `json:"messages,omitempty"`
	BotID            string         `json:"bot_id,omitempty"`
	AgentID          string         `json:"agent_id,omitempty"`
	RunID            string         `json:"run_id,omitempty"`
	Metadata         map[string]any `json:"metadata,omitempty"`
	Filters          map[string]any `json:"filters,omitempty"`
	Infer            *bool          `json:"infer,omitempty"`
	EmbeddingEnabled *bool          `json:"embedding_enabled,omitempty"`
	SourceMessageIDs []string       `json:"source_message_ids,omitempty"`
}

type AfterChatRequest

type AfterChatRequest struct {
	BotID             string
	Messages          []Message
	UserID            string
	ChannelIdentityID string
	DisplayName       string
	TimezoneLocation  *time.Location
	// SkipFormation turns off automatic memory formation for this turn.
	// External agent runtimes own the model that ran the turn: the platform
	// neither borrows another model to extract facts nor dumps the raw
	// transcript into the same store those facts live in. Those runtimes
	// write their own memories through the create_memory tool instead.
	SkipFormation bool
}

AfterChatRequest is passed to OnAfterChat after receiving the gateway response.

type BeforeChatRequest

type BeforeChatRequest struct {
	Query  string
	BotID  string
	ChatID string
}

BeforeChatRequest is passed to OnBeforeChat before sending to the agent gateway.

type BeforeChatResult

type BeforeChatResult struct {
	ContextText    string // formatted text to inject as a user message
	RetrievalMode  string // graph, file_fallback, etc.
	FallbackReason string // non-empty when the provider degraded to another retrieval path
	ResultCount    int    // number of memory items represented in ContextText; zero when item metadata is unavailable
	ResultRefs     []string
}

BeforeChatResult contains memory context to inject into the conversation.

type CandidateMemory

type CandidateMemory struct {
	ID        string         `json:"id"`
	Memory    string         `json:"memory"`
	CreatedAt string         `json:"created_at,omitempty"`
	Metadata  map[string]any `json:"metadata,omitempty"`
}

type CompactRequest

type CompactRequest struct {
	BotID       string            `json:"bot_id,omitempty"`
	Memories    []CandidateMemory `json:"memories"`
	TargetCount int               `json:"target_count"`
	DecayDays   int               `json:"decay_days,omitempty"`
}

type CompactResponse

type CompactResponse struct {
	Facts []string `json:"facts"`
}

type CompactResult

type CompactResult struct {
	BeforeCount int          `json:"before_count"`
	AfterCount  int          `json:"after_count"`
	Ratio       float64      `json:"ratio"`
	Results     []MemoryItem `json:"results"`
}

type DecideRequest

type DecideRequest struct {
	BotID      string            `json:"bot_id,omitempty"`
	Facts      []string          `json:"facts"`
	Candidates []CandidateMemory `json:"candidates"`
	Filters    map[string]any    `json:"filters,omitempty"`
	Metadata   map[string]any    `json:"metadata,omitempty"`
}

type DecideResponse

type DecideResponse struct {
	Actions []DecisionAction `json:"actions"`
}

type DecisionAction

type DecisionAction struct {
	Event             string `json:"event"`
	ID                string `json:"id,omitempty"`
	Text              string `json:"text"`
	OldMemory         string `json:"old_memory,omitempty"`
	SourceFactIndices []int  `json:"source_fact_indices,omitempty"`
}

type DeleteAllRequest

type DeleteAllRequest struct {
	BotID   string         `json:"bot_id,omitempty"`
	AgentID string         `json:"agent_id,omitempty"`
	RunID   string         `json:"run_id,omitempty"`
	Filters map[string]any `json:"filters,omitempty"`
}

type DeleteResponse

type DeleteResponse struct {
	Message string `json:"message"`
}

type ExtractRequest

type ExtractRequest struct {
	BotID            string         `json:"bot_id,omitempty"`
	Messages         []Message      `json:"messages"`
	Filters          map[string]any `json:"filters,omitempty"`
	Metadata         map[string]any `json:"metadata,omitempty"`
	TimezoneLocation *time.Location `json:"-"`
}

type ExtractResponse

type ExtractResponse struct {
	Facts                []string   `json:"facts"`
	FactSourceMessageIDs [][]string `json:"-"`
}

type Factory

type Factory func(ctx context.Context, teamID, id string, config map[string]any) (Provider, error)

Factory creates a Provider from a provider type string and JSON config. The registry uses factories to lazily instantiate providers from DB rows.

type GetAllRequest

type GetAllRequest struct {
	BotID   string         `json:"bot_id,omitempty"`
	AgentID string         `json:"agent_id,omitempty"`
	RunID   string         `json:"run_id,omitempty"`
	Limit   int            `json:"limit,omitempty"`
	Filters map[string]any `json:"filters,omitempty"`
	NoStats bool           `json:"no_stats,omitempty"`
}

type HealthStatus

type HealthStatus struct {
	OK    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
}

type IngestResult

type IngestResult struct {
	// Ingested is the number of memory nodes written to the store (inserts +
	// updates; re-ingesting an unchanged file counts as an update).
	Ingested int `json:"ingested"`
	// Skipped is the number of source items that parsed to empty content or
	// failed to persist.
	Skipped int `json:"skipped"`
}

IngestResult reports the outcome of a file→DB memory ingest pass.

type LLM

type LLM interface {
	Extract(ctx context.Context, req ExtractRequest) (ExtractResponse, error)
	Decide(ctx context.Context, req DecideRequest) (DecideResponse, error)
	Compact(ctx context.Context, req CompactRequest) (CompactResponse, error)
}

LLM is the interface for LLM operations needed by memory service.

type MarkdownIngestProvider

type MarkdownIngestProvider interface {
	IngestFromMarkdown(ctx context.Context, botID string) (IngestResult, error)
}

MarkdownIngestProvider is implemented by providers whose canonical source of truth is the DB but which also accept agent-authored Markdown files as input. IngestFromMarkdown reads /data/memory/*.md and upserts them as DB nodes, closing the gap left by the DB→file derived-view sync (which only writes files FROM nodes). Providers that treat files as the source of truth (e.g. the legacy file runtime) do not implement this.

type MemoryCompactCapability

type MemoryCompactCapability struct {
	Semantic     bool   `json:"semantic"`
	Archive      bool   `json:"archive,omitempty"`
	RebuildIndex bool   `json:"rebuild_index,omitempty"`
	Reason       string `json:"reason,omitempty"`
}

type MemoryConfig added in v0.21.0

type MemoryConfig struct {
	// EmbeddingModelID optionally maintains the pgvector semantic seed index
	// for graph recall. Empty means graph-only recall.
	EmbeddingModelID string `json:"embedding_model_id"`
}

MemoryConfig is the team-level Built-in Memory configuration.

type MemoryConfigUpdateRequest added in v0.21.0

type MemoryConfigUpdateRequest struct {
	EmbeddingModelID *string `json:"embedding_model_id,omitempty"`
}

MemoryConfigUpdateRequest updates the team-level Built-in Memory configuration: nil keeps the current value, "" clears it.

type MemoryContextCache

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

MemoryContextCache stores rendered memory context for fast first-token paths.

func (*MemoryContextCache) Get

Get returns a fresh cache value.

func (*MemoryContextCache) GetFreshOrStale added in v0.20.0

GetFreshOrStale returns an available value and atomically classifies its age.

func (*MemoryContextCache) GetStale

GetStale returns a value inside its stale grace window.

func (*MemoryContextCache) Set

Set stores a rendered memory context.

type MemoryContextCacheConfig

type MemoryContextCacheConfig struct {
	TTL        time.Duration
	StaleTTL   time.Duration
	MaxEntries int
	Now        func() time.Time
}

MemoryContextCacheConfig configures the short-lived chat memory context cache. StaleTTL is the additional grace window after TTL expires.

type MemoryContextCacheKey

type MemoryContextCacheKey struct {
	BotID         string
	ChatID        string
	ProviderID    string
	QueryHash     string
	MemoryVersion string
}

MemoryContextCacheKey identifies one rendered memory context payload.

type MemoryContextCacheState added in v0.20.0

type MemoryContextCacheState string

MemoryContextCacheState classifies an available cache entry by age.

const (
	MemoryContextCacheFresh MemoryContextCacheState = "fresh"
	MemoryContextCacheStale MemoryContextCacheState = "stale"
)

type MemoryContextCacheValue

type MemoryContextCacheValue struct {
	ContextText    string
	RetrievalMode  string
	FallbackReason string
	ResultCount    int
	ResultRefs     []string
	CreatedAt      time.Time
	ExpiresAt      time.Time
	StaleUntil     time.Time
	LastAccessedAt time.Time
}

MemoryContextCacheValue is a cached rendered memory context.

type MemoryItem

type MemoryItem struct {
	ID        string         `json:"id"`
	Memory    string         `json:"memory"`
	Hash      string         `json:"hash,omitempty"`
	CreatedAt string         `json:"created_at,omitempty"`
	UpdatedAt string         `json:"updated_at,omitempty"`
	Score     float64        `json:"score,omitempty"`
	Metadata  map[string]any `json:"metadata,omitempty"`
	BotID     string         `json:"bot_id,omitempty"`
	AgentID   string         `json:"agent_id,omitempty"`
	RunID     string         `json:"run_id,omitempty"`
	// SourceMessageIDs is internal provenance. Public HTTP responses must not
	// expose raw session/message locators; tool projections authorize and render
	// them explicitly at the request boundary.
	SourceMessageIDs []string `json:"-"`
}

func DeduplicateItems

func DeduplicateItems(items []MemoryItem) []MemoryItem

DeduplicateItems removes duplicate MemoryItems by ID.

type MemoryStatusResponse

type MemoryStatusResponse struct {
	ProviderType      string                  `json:"provider_type,omitempty"`
	MemoryMode        string                  `json:"memory_mode,omitempty"`
	Compact           MemoryCompactCapability `json:"compact"`
	CanManualSync     bool                    `json:"can_manual_sync"`
	SourceDir         string                  `json:"source_dir,omitempty"`
	OverviewPath      string                  `json:"overview_path,omitempty"`
	MarkdownFileCount int                     `json:"markdown_file_count"`
	SourceCount       int                     `json:"source_count"`
	EdgeCount         int                     `json:"edge_count"`
	IndexedCount      int                     `json:"indexed_count"`
	VectorIndex       string                  `json:"vector_index,omitempty"`
	Encoder           *HealthStatus           `json:"encoder,omitempty"`
	Pgvector          *HealthStatus           `json:"pgvector,omitempty"`
	// Degraded reports that the semantic seed index is behind the wiki store
	// (failed upserts are queued for retry); graph recall still works.
	Degraded        bool `json:"degraded"`
	RetryQueueDepth int  `json:"retry_queue_depth"`
}

type MemoryVersionProvider

type MemoryVersionProvider interface {
	MemoryVersion(ctx context.Context, botID string) string
}

MemoryVersionProvider is implemented by providers that can expose a cheap cache-busting version for the bot's memory source of truth.

type Message

type Message struct {
	Role            string `json:"role"`
	Content         string `json:"content"`
	SourceMessageID string `json:"-"`
}

type Provider

type Provider interface {
	// Type returns the provider type identifier ("builtin").
	Type() string

	OnBeforeChat(ctx context.Context, req BeforeChatRequest) (*BeforeChatResult, error)
	OnAfterChat(ctx context.Context, req AfterChatRequest) error

	ListTools(ctx context.Context, session mcp.ToolSessionContext) ([]mcp.ToolDescriptor, error)
	CallTool(ctx context.Context, session mcp.ToolSessionContext, toolName string, arguments map[string]any) (map[string]any, error)

	Add(ctx context.Context, req AddRequest) (SearchResponse, error)
	Search(ctx context.Context, req SearchRequest) (SearchResponse, error)
	GetAll(ctx context.Context, req GetAllRequest) (SearchResponse, error)
	// Update/Delete/DeleteBatch require the authorized bot, independently of caller-supplied IDs.
	Update(ctx context.Context, req UpdateRequest) (MemoryItem, error)
	Delete(ctx context.Context, botID string, memoryID string) (DeleteResponse, error)
	DeleteBatch(ctx context.Context, botID string, memoryIDs []string) (DeleteResponse, error)
	DeleteAll(ctx context.Context, req DeleteAllRequest) (DeleteResponse, error)

	Compact(ctx context.Context, filters map[string]any, ratio float64, decayDays int) (CompactResult, error)
	Usage(ctx context.Context, filters map[string]any) (UsageResponse, error)
}

Provider is the interface the Built-in Memory runtime implements for chat hooks, memory tools, and management operations.

type ProviderConfigLoader

type ProviderConfigLoader func(ctx context.Context, id string) (providerType string, config map[string]any, err error)

ProviderConfigLoader loads one provider configuration under the team already bound to ctx. It lets a registry lazily instantiate providers on first use instead of requiring an all-team startup scan.

type ProviderGetResponse

type ProviderGetResponse struct {
	ID        string         `json:"id"`
	Name      string         `json:"name"`
	Provider  string         `json:"provider"`
	Config    map[string]any `json:"config,omitempty"`
	IsDefault bool           `json:"is_default"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
}

type ProviderType

type ProviderType string

Memory provider storage types. Built-in Memory is the only provider type; memory_providers rows store the team's Built-in Memory configuration, and a bot's memory_provider_id is non-null exactly when its memory is enabled.

const ProviderBuiltin ProviderType = "builtin"

type RebuildResult

type RebuildResult struct {
	FsCount       int `json:"fs_count"`
	StorageCount  int `json:"storage_count"`
	MissingCount  int `json:"missing_count"`
	RestoredCount int `json:"restored_count"`
}

type Registry

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

Registry manages provider instances keyed by their DB id. It caches instantiated providers and uses registered factories to create them on demand from stored configuration.

func NewRegistry

func NewRegistry(log *slog.Logger, resolvers ...TeamIDResolver) *Registry

func (*Registry) Close

func (r *Registry) Close() error

Close releases every instantiated provider. It is safe to call more than once and is used during process shutdown as well as team registry teardown.

func (*Registry) Get

func (r *Registry) Get(ctx context.Context, id string) (Provider, error)

Get returns the team-owned provider for the given DB record ID. Cache misses are loaded and instantiated under the same team scope.

func (*Registry) Instantiate

func (r *Registry) Instantiate(ctx context.Context, id, providerType string, config map[string]any) (Provider, error)

Instantiate creates a provider from a DB row and caches it. If the instance already exists, it is returned directly.

func (*Registry) Register

func (r *Registry) Register(id string, provider Provider)

Register adds a pre-built provider instance by ID.

func (*Registry) RegisterContext

func (r *Registry) RegisterContext(ctx context.Context, id string, provider Provider) error

RegisterContext adds a pre-built provider under the team resolved from ctx.

func (*Registry) RegisterFactory

func (r *Registry) RegisterFactory(providerType string, factory Factory)

RegisterFactory registers a factory for a given provider type (e.g. "builtin").

func (*Registry) Remove

func (r *Registry) Remove(ctx context.Context, id string) error

Remove evicts a cached provider instance (e.g. after config update or delete).

func (*Registry) SetConfigLoader

func (r *Registry) SetConfigLoader(loader ProviderConfigLoader)

SetConfigLoader configures lazy provider lookup for cache misses.

func (*Registry) SetTeamDefaultFactory

func (r *Registry) SetTeamDefaultFactory(factory TeamDefaultFactory)

SetTeamDefaultFactory configures lazy, team-owned builtin fallbacks.

type SearchRequest

type SearchRequest struct {
	Query            string         `json:"query"`
	BotID            string         `json:"bot_id,omitempty"`
	AgentID          string         `json:"agent_id,omitempty"`
	RunID            string         `json:"run_id,omitempty"`
	Limit            int            `json:"limit,omitempty"`
	Filters          map[string]any `json:"filters,omitempty"`
	Sources          []string       `json:"sources,omitempty"`
	EmbeddingEnabled *bool          `json:"embedding_enabled,omitempty"`
	NoStats          bool           `json:"no_stats,omitempty"`
}

type SearchResponse

type SearchResponse struct {
	Results        []MemoryItem `json:"results"`
	Relations      []any        `json:"relations,omitempty"`
	RetrievalMode  string       `json:"retrieval_mode,omitempty"`
	FallbackReason string       `json:"fallback_reason,omitempty"`
}

type SemanticCompactProvider

type SemanticCompactProvider interface {
	SemanticCompactCapability() MemoryCompactCapability
}

SemanticCompactProvider is implemented by providers that can apply Memoh's semantic memory compact contract under the selected bot scope.

type Service

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

func NewService

func NewService(log *slog.Logger, queries dbstore.Queries, cfg config.Config) *Service

func (*Service) EnsureBuiltin added in v0.21.0

func (s *Service) EnsureBuiltin(ctx context.Context) (ProviderGetResponse, error)

EnsureBuiltin returns the team's Built-in Memory row, creating it on first use.

func (*Service) EnsureBuiltinID added in v0.21.0

func (s *Service) EnsureBuiltinID(ctx context.Context) (string, error)

EnsureBuiltinID returns the ID a memory-enabled bot should reference.

func (*Service) Get

func (*Service) GetConfig added in v0.21.0

func (s *Service) GetConfig(ctx context.Context) (MemoryConfig, error)

GetConfig returns the team's Built-in Memory configuration. A team that has never saved one reads as the zero configuration.

func (*Service) SetRegistry

func (s *Service) SetRegistry(registry *Registry)

SetRegistry configures the runtime registry so that configuration updates can evict and re-instantiate provider instances automatically.

func (*Service) UpdateConfig added in v0.21.0

func (s *Service) UpdateConfig(ctx context.Context, req MemoryConfigUpdateRequest) (MemoryConfig, error)

UpdateConfig saves the team's Built-in Memory configuration, creating the builtin row on first use. Stored keys the request does not cover are kept.

type SourceSyncProvider

type SourceSyncProvider interface {
	Status(ctx context.Context, botID string) (MemoryStatusResponse, error)
	Rebuild(ctx context.Context, botID string) (RebuildResult, error)
}

SourceSyncProvider is implemented by providers that can report runtime status and rebuild derived storage from a canonical source of truth.

type TeamDefaultFactory

type TeamDefaultFactory func(ctx context.Context, teamID string) (Provider, error)

TeamDefaultFactory creates the builtin fallback independently for each team. It is used for bots without an explicit memory provider.

type TeamIDResolver

type TeamIDResolver func(context.Context) (string, error)

TeamIDResolver resolves the team that owns a memory operation. Hosted deployments can inject a strict request-context resolver; upstream defaults to its published singleton team.

func FixedTeamIDResolver

func FixedTeamIDResolver(teamID string) TeamIDResolver

FixedTeamIDResolver returns a resolver permanently scoped to teamID. Provider factories use this for team-owned runtimes whose background work may outlive the request context that instantiated them.

type UpdateRequest

type UpdateRequest struct {
	// BotID is the trusted caller scope, never inferred from MemoryID.
	BotID            string   `json:"bot_id"`
	MemoryID         string   `json:"memory_id"`
	Memory           string   `json:"memory"`
	EmbeddingEnabled *bool    `json:"embedding_enabled,omitempty"`
	SourceMessageIDs []string `json:"source_message_ids,omitempty"`
}

type UsageResponse

type UsageResponse struct {
	Count                 int   `json:"count"`
	TotalTextBytes        int64 `json:"total_text_bytes"`
	AvgTextBytes          int64 `json:"avg_text_bytes"`
	EstimatedStorageBytes int64 `json:"estimated_storage_bytes"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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