vector

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2026 License: Apache-2.0 Imports: 12 Imported by: 1

Documentation

Overview

Package vector provides embeddings, chunking, similarity search, and an in-process vector store for goagent's long-term memory subsystem.

Overview

The package is built around four orthogonal concerns:

  1. Embedding — converting message content into dense float32 vectors.
  2. Chunking — splitting content into pieces that fit a model's context window.
  3. Similarity — measuring how close two vectors are in semantic space.
  4. Storage — persisting (id, vector, message) triples and searching by similarity.

Embedders

OllamaEmbedder calls a local Ollama server and supports any text embedding model (e.g. "nomic-embed-text"):

e := vector.NewOllamaEmbedder("nomic-embed-text")

FallbackEmbedder filters blocks by type before delegating to a primary embedder, enabling text-only embedders to gracefully handle multimodal input:

fe := vector.NewFallbackEmbedder(
    vector.NewOllamaEmbedder("nomic-embed-text"),
    vector.WithSupportedType(goagent.ContentText),
)

Chunkers

NoOpChunker (default) passes content through as a single chunk:

c := vector.NewNoOpChunker()

TextChunker splits text at word boundaries with configurable overlap:

c := vector.NewTextChunker(
    vector.WithMaxSize(500),
    vector.WithOverlap(50),
)

BlockChunker processes mixed content (text, images, PDFs) per block:

c := vector.NewBlockChunker(vector.WithPDFExtractor(myExtractor))

Estimators

Estimators measure text size in model-specific units for the Chunker:

&vector.HeuristicTokenEstimator{} // default, ±15% error, no deps
&vector.CharEstimator{}           // character count
vector.NewOllamaTokenEstimator("nomic-embed-text") // exact, via Ollama

Storage

InMemoryStore is a thread-safe in-process store suitable for development and small deployments (< ~10,000 entries). Search is O(n):

store := vector.NewInMemoryStore()

Session filtering

InMemoryStore.Search can filter results to a single session when the context carries a session ID (injected via internal/session.NewContext). Agent.Run sets this automatically when WithName is configured. IDs must follow the "sessionID:baseID:chunkIndex" convention used by memory.LongTermMemory. session.NewContext rejects IDs containing ":" so the first ":" always marks the boundary between the session prefix and the base ID:

ctx, err := session.NewContext(ctx, "user-42")
msgs, err := store.Search(ctx, queryVec, 3)

Similarity

CosineSimilarity assumes unit-length vectors (as returned by most modern models). Use CosineSimilarityRaw for non-normalized vectors, or call Normalize explicitly:

v := vector.Normalize(rawVec)
score := vector.CosineSimilarity(v, queryVec)

Index

Constants

This section is empty.

Variables

View Source
var ErrNoEmbeddeableContent = errors.New("vector: no embeddeable content in blocks")

ErrNoEmbeddeableContent is returned when none of the input blocks can be processed by the Embedder (e.g. a text-only embedder receives only images). This is not a fatal error in Store — callers may skip the chunk with errors.Is.

Functions

func ChunkToMessage

func ChunkToMessage(orig goagent.Message, chunk ChunkResult) goagent.Message

ChunkToMessage builds a Message from an original message and a ChunkResult. The returned message preserves the original Role and ToolCallID but replaces Content with the chunk's blocks.

func CosineSimilarity

func CosineSimilarity(a, b []float32) float64

CosineSimilarity computes the cosine similarity between two normalized vectors. Both vectors must have the same length. For unit-length vectors this is equivalent to the dot product — O(n), no sqrt required. Returns values in the range [-1, 1].

func CosineSimilarityRaw

func CosineSimilarityRaw(a, b []float32) float64

CosineSimilarityRaw computes cosine similarity for vectors that are NOT unit-length. It computes norms in the same pass as the dot product. Returns 0 if either vector is the zero vector.

func EmbedAll

func EmbedAll(ctx context.Context, e goagent.Embedder, chunks []ChunkResult) ([][]float32, error)

EmbedAll embeds each ChunkResult and returns the vectors in the same order. If any chunk fails, it returns an error identifying the failing chunk index. Does not parallelize — the caller is responsible for concurrency.

func ExtractText

func ExtractText(blocks []goagent.ContentBlock) string

ExtractText is the exported counterpart of extractText. It concatenates all ContentText blocks from blocks separated by spaces.

func Normalize

func Normalize(v []float32) []float32

Normalize scales v to unit length (L2 norm = 1.0). Returns a new slice — the input is not modified. If v is the zero vector, returns v unchanged (a zero vector has no direction). Most modern embedding models (nomic-embed-text, voyage-3) already return unit-length vectors; call Normalize only when the model does not.

Types

type BlockChunker

type BlockChunker struct {
	TextMaxSize   int
	TextOverlap   int
	TextEstimator SizeEstimator
	PDFExtractor  PDFExtractor // nil disables page extraction
}

BlockChunker generates chunks per ContentBlock, preserving block types. Long text blocks are sub-divided using TextChunker logic. Images pass through unchanged — they are never split. PDFs are split per page when a PDFExtractor is configured. Requires a multimodal Embedder capable of processing images (Phase 3+).

func NewBlockChunker

func NewBlockChunker(opts ...BlockChunkerOption) *BlockChunker

NewBlockChunker creates a BlockChunker with defaults: MaxSize=500, Overlap=50, Estimator=HeuristicTokenEstimator.

func (*BlockChunker) Chunk

func (c *BlockChunker) Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)

Chunk splits content into typed chunks.

type BlockChunkerOption

type BlockChunkerOption func(*BlockChunker)

BlockChunkerOption configures a BlockChunker.

func WithBlockEstimator

func WithBlockEstimator(e SizeEstimator) BlockChunkerOption

WithBlockEstimator sets the SizeEstimator for text blocks. Default: HeuristicTokenEstimator.

func WithBlockMaxSize

func WithBlockMaxSize(n int) BlockChunkerOption

WithBlockMaxSize sets the maximum text chunk size in estimator units. Default: 500.

func WithBlockOverlap

func WithBlockOverlap(n int) BlockChunkerOption

WithBlockOverlap sets the text overlap in estimator units. Default: 50.

func WithPDFExtractor

func WithPDFExtractor(e PDFExtractor) BlockChunkerOption

WithPDFExtractor sets the PDFExtractor for document blocks. Without an extractor, document blocks pass through as single chunks.

type CharEstimator

type CharEstimator struct{}

CharEstimator measures text size in Unicode code points (runes). No external dependencies. Useful when the model limit is expressed in characters rather than tokens.

func (*CharEstimator) Measure

func (e *CharEstimator) Measure(_ context.Context, text string) int

Measure returns the number of Unicode code points in text.

type ChunkContent

type ChunkContent struct {
	Blocks   []goagent.ContentBlock
	Metadata map[string]any
}

ChunkContent is the input to a Chunker. Blocks holds the content to be divided. Metadata carries source information that is propagated to every ChunkResult.

type ChunkResult

type ChunkResult struct {
	Blocks   []goagent.ContentBlock
	Metadata map[string]any
}

ChunkResult is the output of a Chunker — a piece ready for embedding. Metadata inherits from ChunkContent and may include chunk_index, chunk_total, page, source, etc., depending on the Chunker implementation.

type Chunker

type Chunker interface {
	Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)
}

Chunker splits a ChunkContent into pieces that can be embedded individually. Implementations decide how to handle each ContentBlock type.

type FallbackEmbedder

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

FallbackEmbedder wraps a primary Embedder and filters out blocks whose type is not the configured supported type. If at least one block survives the filter, it delegates to the primary Embedder. If none survive, it returns ErrNoEmbeddeableContent. Facilitates the transition from text-only to multimodal embedders without changing the calling API.

func NewFallbackEmbedder

func NewFallbackEmbedder(primary goagent.Embedder, opts ...FallbackEmbedderOption) *FallbackEmbedder

NewFallbackEmbedder creates a FallbackEmbedder wrapping primary. Default supported type: ContentText.

func (*FallbackEmbedder) Embed

func (e *FallbackEmbedder) Embed(ctx context.Context, blocks []goagent.ContentBlock) ([]float32, error)

Embed filters blocks by the configured supported type and delegates to the primary Embedder. Returns ErrNoEmbeddeableContent when no blocks survive the filter.

type FallbackEmbedderOption

type FallbackEmbedderOption func(*FallbackEmbedder)

FallbackEmbedderOption configures a FallbackEmbedder.

func WithOnSkipped

func WithOnSkipped(fn func(goagent.ContentBlock)) FallbackEmbedderOption

WithOnSkipped registers a callback invoked for each block filtered out because its type is not the supported type. May be nil (default).

func WithSupportedType

func WithSupportedType(t goagent.ContentType) FallbackEmbedderOption

WithSupportedType sets the content type that the primary Embedder can handle. Blocks of other types are filtered out before calling Embed. Default: ContentText.

type HeuristicTokenEstimator

type HeuristicTokenEstimator struct{}

HeuristicTokenEstimator estimates token count using the heuristic len(text)/4 + 4. Typical error: ±15% compared to a real tokenizer. No external dependencies. Recommended default for most use cases. Uses the same formula as memory/policy.TokenWindowPolicy.

func (*HeuristicTokenEstimator) Measure

func (e *HeuristicTokenEstimator) Measure(_ context.Context, text string) int

Measure returns an estimated token count for text.

type InMemoryStore

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

InMemoryStore implements goagent.VectorStore using an in-process slice protected by sync.RWMutex. Search is O(n) — suitable for development, testing, and deployments with fewer than ~10,000 entries. For production at larger scale use a dedicated vector database.

func NewInMemoryStore

func NewInMemoryStore() *InMemoryStore

NewInMemoryStore creates an empty InMemoryStore.

func (*InMemoryStore) Search

func (s *InMemoryStore) Search(ctx context.Context, query []float32, topK int) ([]goagent.Message, error)

Search returns the topK messages most similar to the query vector, ordered by cosine similarity descending. If the context carries a session ID (set via WithSessionID), only entries whose id starts with "sessionID:" are considered. Returns fewer than topK results when the store contains fewer matching entries.

func (*InMemoryStore) Upsert

func (s *InMemoryStore) Upsert(_ context.Context, id string, vec []float32, msg goagent.Message) error

Upsert stores or replaces the message and its embedding under id. The operation is idempotent: calling Upsert twice with the same id replaces the first entry with the second. A copy of the vector is made to protect against caller mutation.

type NoOpChunker

type NoOpChunker struct{}

NoOpChunker returns its input as a single unchanged chunk. Default for conversational messages whose size rarely exceeds model limits.

func NewNoOpChunker

func NewNoOpChunker() *NoOpChunker

NewNoOpChunker creates a NoOpChunker.

func (*NoOpChunker) Chunk

func (c *NoOpChunker) Chunk(_ context.Context, content ChunkContent) ([]ChunkResult, error)

Chunk returns content as a single ChunkResult without modification.

type OllamaTokenEstimator

type OllamaTokenEstimator struct {
	BaseURL string
	Model   string
	// contains filtered or unexported fields
}

OllamaTokenEstimator obtains the exact token count via the Ollama tokenize API. Not suitable for hot paths — use in batch preprocessing or document ingestion only. The caller controls cancellation and timeouts via ctx. On any error (network, parse, cancellation) it silently falls back to HeuristicTokenEstimator.

func NewOllamaTokenEstimator

func NewOllamaTokenEstimator(model string) *OllamaTokenEstimator

NewOllamaTokenEstimator creates an OllamaTokenEstimator for the given model. The Ollama server is expected at http://localhost:11434.

func (*OllamaTokenEstimator) Measure

func (e *OllamaTokenEstimator) Measure(ctx context.Context, text string) int

Measure returns the token count from Ollama, falling back to the heuristic estimate on any error. ctx controls the lifetime of the HTTP request.

type PDFExtractor

type PDFExtractor interface {
	ExtractPages(ctx context.Context, data []byte) ([]PDFPage, error)
}

PDFExtractor extracts page content from a PDF document. Implementations may return text (PDFs with embedded text), images (scanned PDFs or pages with diagrams), or both.

type PDFPage

type PDFPage struct {
	Text  string
	Image []byte
}

PDFPage represents the content of a single extracted page. Text is empty when the page contains only images. Image is nil when the page has extractable text.

type PageChunker

type PageChunker struct {
	PDFExtractor PDFExtractor
}

PageChunker splits PDF documents into per-page chunks. Non-document blocks pass through unchanged. Each chunk carries metadata with "page", "total_pages", and "source". For v0.4 use only when processing PDFs with extractable text.

func NewPageChunker

func NewPageChunker(opts ...PageChunkerOption) *PageChunker

NewPageChunker creates a PageChunker with the given options.

func (*PageChunker) Chunk

func (c *PageChunker) Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)

Chunk splits document blocks by page; non-document blocks pass through.

type PageChunkerOption

type PageChunkerOption func(*PageChunker)

PageChunkerOption configures a PageChunker.

func WithPagePDFExtractor

func WithPagePDFExtractor(e PDFExtractor) PageChunkerOption

WithPagePDFExtractor sets the PDFExtractor for the PageChunker.

type SizeEstimator

type SizeEstimator interface {
	Measure(ctx context.Context, text string) int
}

SizeEstimator measures the size of a text string in implementation-defined units. The units determine the semantics of MaxSize and Overlap in chunkers. ctx is passed through so implementations that perform I/O (e.g. OllamaTokenEstimator) can be cancelled or time-limited by the caller.

type TextChunker

type TextChunker struct {
	MaxSize   int           // maximum chunk size in Estimator units
	Overlap   int           // overlap between adjacent chunks in Estimator units
	Estimator SizeEstimator // default: HeuristicTokenEstimator
}

TextChunker extracts ContentText blocks and splits them when they exceed MaxSize. ContentImage and ContentDocument blocks are silently ignored. Compatible with any text-only Embedder.

func NewTextChunker

func NewTextChunker(opts ...TextChunkerOption) *TextChunker

NewTextChunker creates a TextChunker with defaults: MaxSize=500, Overlap=50, Estimator=HeuristicTokenEstimator.

func (*TextChunker) Chunk

func (c *TextChunker) Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)

Chunk splits content into text chunks. Returns nil when there is no text.

type TextChunkerOption

type TextChunkerOption func(*TextChunker)

TextChunkerOption configures a TextChunker.

func WithEstimator

func WithEstimator(e SizeEstimator) TextChunkerOption

WithEstimator sets the SizeEstimator used to measure chunk size. Default: HeuristicTokenEstimator.

func WithMaxSize

func WithMaxSize(n int) TextChunkerOption

WithMaxSize sets the maximum chunk size in estimator units. Default: 500.

func WithOverlap

func WithOverlap(n int) TextChunkerOption

WithOverlap sets the number of overlapping units between adjacent chunks. Default: 50.

Directories

Path Synopsis
pgvector module
sqlitevec module

Jump to

Keyboard shortcuts

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