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:
- Embedding — converting message content into dense float32 vectors.
- Chunking — splitting content into pieces that fit a model's context window.
- Similarity — measuring how close two vectors are in semantic space.
- 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),
)
SentenceChunker splits text at sentence boundaries, grouping complete sentences until the size limit is reached. Overlap is counted in sentences, not tokens, which keeps adjacent chunks semantically coherent:
c := vector.NewSentenceChunker(
vector.WithSentenceMaxSize(300),
vector.WithSentenceOverlap(2),
)
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") results, err := store.Search(ctx, queryVec, 3) // results[i].Message holds the message; results[i].Score is cosine similarity.
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)
Example ¶
Example demonstrates splitting a text document into chunks with the default TextChunker. Short texts that fit within MaxSize are returned as a single chunk.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
chunker := vector.NewTextChunker()
content := vector.ChunkContent{
Blocks: []goagent.ContentBlock{
goagent.TextBlock("Go is a compiled, statically typed language designed for simplicity."),
},
}
chunks, err := chunker.Chunk(ctx, content)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(chunks))
}
Output: 1
Index ¶
- Variables
- func ChunkToMessage(orig goagent.Message, chunk ChunkResult) goagent.Message
- func CosineSimilarity(a, b []float32) float64
- func CosineSimilarityRaw(a, b []float32) float64
- func EmbedAll(ctx context.Context, e goagent.Embedder, chunks []ChunkResult) ([][]float32, error)
- func ExtractText(blocks []goagent.ContentBlock) string
- func Normalize(v []float32) []float32
- type BlockChunker
- type BlockChunkerOption
- type CharEstimator
- type ChunkContent
- type ChunkResult
- type Chunker
- type FallbackEmbedder
- type FallbackEmbedderOption
- type HeuristicTokenEstimator
- type InMemoryStore
- func (s *InMemoryStore) BulkDelete(_ context.Context, ids []string) error
- func (s *InMemoryStore) BulkUpsert(_ context.Context, entries []goagent.UpsertEntry) error
- func (s *InMemoryStore) Count(ctx context.Context, opts ...goagent.SearchOption) (int64, error)
- func (s *InMemoryStore) Delete(_ context.Context, id string) error
- func (s *InMemoryStore) Search(ctx context.Context, query []float32, topK int, opts ...goagent.SearchOption) ([]goagent.ScoredMessage, error)
- func (s *InMemoryStore) Upsert(_ context.Context, id string, vec []float32, msg goagent.Message) error
- type NoOpChunker
- type OllamaTokenEstimator
- type PDFExtractor
- type PDFPage
- type PageChunker
- type PageChunkerOption
- type RecursiveChunker
- type RecursiveChunkerOption
- type SentenceChunker
- type SentenceChunkerOption
- type SizeEstimator
- type TextChunker
- type TextChunkerOption
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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.
Example ¶
ExampleChunkToMessage shows building a Message from a ChunkResult while preserving the original Role.
package main
import (
"fmt"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
orig := goagent.AssistantMessage("original content")
chunk := vector.ChunkResult{
Blocks: []goagent.ContentBlock{goagent.TextBlock("chunk content")},
Metadata: map[string]any{"chunk_index": 0},
}
msg := vector.ChunkToMessage(orig, chunk)
fmt.Println(msg.TextContent())
}
Output: chunk content
func CosineSimilarity ¶
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].
Example ¶
ExampleCosineSimilarity shows computing the dot-product similarity between two unit-length vectors — equivalent to cosine similarity for normalized vectors.
package main
import (
"fmt"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
a := []float32{1, 0, 0}
b := []float32{1, 0, 0}
fmt.Printf("%.1f\n", vector.CosineSimilarity(a, b)) // identical direction
c := []float32{1, 0, 0}
d := []float32{0, 1, 0}
fmt.Printf("%.1f\n", vector.CosineSimilarity(c, d)) // orthogonal
}
Output: 1.0 0.0
func CosineSimilarityRaw ¶
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 ¶
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.
Example ¶
ExampleExtractText shows concatenating all ContentText blocks while ignoring non-text blocks such as images.
package main
import (
"fmt"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
blocks := []goagent.ContentBlock{
goagent.TextBlock("hello"),
goagent.ImageBlock([]byte{0xFF, 0xD8}, "image/jpeg"), // non-text, silently ignored
goagent.TextBlock("world"),
}
fmt.Println(vector.ExtractText(blocks))
}
Output: hello world
func Normalize ¶
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.
Example ¶
ExampleNormalize shows scaling a vector to unit length (L2 norm = 1).
package main
import (
"fmt"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
v := []float32{3, 4} // norm = 5
n := vector.Normalize(v)
fmt.Printf("%.1f\n", n[0]) // 3/5
fmt.Printf("%.1f\n", n[1]) // 4/5
}
Output: 0.6 0.8
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.
Example ¶
ExampleCharEstimator shows measuring text size in Unicode code points. Useful when the model limit is expressed in characters rather than tokens.
package main
import (
"context"
"fmt"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
est := &vector.CharEstimator{}
fmt.Println(est.Measure(ctx, "hello"))
fmt.Println(est.Measure(ctx, "日本語"))
}
Output: 5 3
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.
Example ¶
ExampleNewFallbackEmbedder shows filtering out non-text blocks before delegating to the primary embedder, and counting skipped blocks.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/internal/testutil"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
skipped := 0
emb := vector.NewFallbackEmbedder(
&testutil.MockEmbedder{},
vector.WithSupportedType(goagent.ContentText),
vector.WithOnSkipped(func(_ goagent.ContentBlock) { skipped++ }),
)
blocks := []goagent.ContentBlock{
goagent.TextBlock("embeddable text"),
goagent.ImageBlock([]byte{0xFF}, "image/png"), // skipped — not ContentText
}
vec, err := emb.Embed(context.Background(), blocks)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(vec) > 0)
fmt.Println(skipped)
}
Output: true 1
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.
Example ¶
ExampleHeuristicTokenEstimator shows the fast token estimator that uses len(text)/4 + 4. Typical error is ±15% compared to a real tokenizer.
package main
import (
"context"
"fmt"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
est := &vector.HeuristicTokenEstimator{}
fmt.Println(est.Measure(ctx, "")) // 0/4 + 4 = 4
fmt.Println(est.Measure(ctx, "hello world")) // 11/4 + 4 = 6
}
Output: 4 6
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.
Example ¶
ExampleNewInMemoryStore shows upserting a vector, counting entries, and searching for the nearest neighbour.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
store := vector.NewInMemoryStore()
msg := goagent.AssistantMessage("vector databases store embeddings")
vec := []float32{1, 0, 0} // unit-length vector
if err := store.Upsert(ctx, "doc:0", vec, msg); err != nil {
log.Fatal(err)
}
count, _ := store.Count(ctx)
fmt.Println(count)
results, err := store.Search(ctx, []float32{1, 0, 0}, 1)
if err != nil {
log.Fatal(err)
}
fmt.Println(results[0].Message.TextContent())
}
Output: 1 vector databases store embeddings
func (*InMemoryStore) BulkDelete ¶ added in v0.5.4
func (s *InMemoryStore) BulkDelete(_ context.Context, ids []string) error
BulkDelete removes all entries with the given ids in a single lock acquisition. IDs that do not exist are silently ignored.
func (*InMemoryStore) BulkUpsert ¶ added in v0.5.4
func (s *InMemoryStore) BulkUpsert(_ context.Context, entries []goagent.UpsertEntry) error
BulkUpsert stores or replaces all entries under their respective ids in a single lock acquisition. A copy of each vector is made to protect against caller mutation.
func (*InMemoryStore) Count ¶ added in v0.5.5
func (s *InMemoryStore) Count(ctx context.Context, opts ...goagent.SearchOption) (int64, error)
Count returns the number of entries in the store that satisfy the given options. Session scope and metadata filter follow the same rules as Search. WithScoreThreshold is ignored because there is no query vector.
func (*InMemoryStore) Delete ¶ added in v0.5.3
func (s *InMemoryStore) Delete(_ context.Context, id string) error
Delete removes the entry with the given id from the store. It is a no-op if id does not exist.
func (*InMemoryStore) Search ¶
func (s *InMemoryStore) Search(ctx context.Context, query []float32, topK int, opts ...goagent.SearchOption) ([]goagent.ScoredMessage, error)
Search returns the topK messages most similar to the query vector, ordered by cosine similarity descending, each paired with its cosine similarity score. 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.
WithScoreThreshold is applied before topK truncation. WithFilter matches against each message's Metadata map using deep equality: every key-value pair in the filter must be present in Metadata. Messages without metadata are excluded when a filter is active.
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.
Example ¶
ExampleNewNoOpChunker shows a chunker that returns its input as a single unchanged chunk — useful when the content already fits within model limits.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
chunker := vector.NewNoOpChunker()
content := vector.ChunkContent{
Blocks: []goagent.ContentBlock{goagent.TextBlock("original text")},
Metadata: map[string]any{"source": "doc.md"},
}
chunks, err := chunker.Chunk(ctx, content)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(chunks))
fmt.Println(vector.ExtractText(chunks[0].Blocks))
}
Output: 1 original text
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.
Example ¶
ExampleNewOllamaTokenEstimator shows constructing an estimator that calls the Ollama tokenize API for exact token counts. No Output: because it makes HTTP requests to a local Ollama server.
package main
import (
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
est := vector.NewOllamaTokenEstimator("llama3.2")
_ = est
}
Output:
type PDFExtractor ¶
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 ¶
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 RecursiveChunker ¶ added in v0.5.1
type RecursiveChunker struct {
// contains filtered or unexported fields
}
RecursiveChunker divides text by respecting a hierarchy of separators. It tries high-level separators first (\n\n, \n) and falls back to sentences and words only when necessary. Ideal for Markdown, documentation, and paragraph-structured text.
Overlap is applied post-split: the tail of chunk[i] is prepended to chunk[i+1] to preserve context at boundaries.
func NewRecursiveChunker ¶ added in v0.5.1
func NewRecursiveChunker(opts ...RecursiveChunkerOption) *RecursiveChunker
NewRecursiveChunker creates a RecursiveChunker with sensible defaults: separators=defaultSeparators, maxSize=500, overlap=50, estimator=HeuristicTokenEstimator.
Example ¶
ExampleNewRecursiveChunker shows splitting a two-paragraph document at the paragraph boundary (\n\n) before falling back to finer separators.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
chunker := vector.NewRecursiveChunker(
vector.WithRCMaxSize(30),
vector.WithRCOverlap(0),
vector.WithRCEstimator(&vector.CharEstimator{}),
)
content := vector.ChunkContent{
Blocks: []goagent.ContentBlock{
goagent.TextBlock("First paragraph text here.\n\nSecond paragraph text here."),
},
}
chunks, err := chunker.Chunk(ctx, content)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(chunks))
fmt.Println(vector.ExtractText(chunks[0].Blocks))
fmt.Println(vector.ExtractText(chunks[1].Blocks))
}
Output: 2 First paragraph text here. Second paragraph text here.
func (*RecursiveChunker) Chunk ¶ added in v0.5.1
func (c *RecursiveChunker) Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)
Chunk splits content into hierarchically-aware chunks. ContentImage and ContentDocument blocks are silently ignored. Returns nil when there is no text. When all text fits in a single chunk, chunk_index and chunk_total are not added to the metadata.
type RecursiveChunkerOption ¶ added in v0.5.1
type RecursiveChunkerOption func(*RecursiveChunker)
RecursiveChunkerOption configures a RecursiveChunker.
func WithRCEstimator ¶ added in v0.5.1
func WithRCEstimator(e SizeEstimator) RecursiveChunkerOption
WithRCEstimator sets the SizeEstimator used to measure text length. Default: HeuristicTokenEstimator.
func WithRCMaxSize ¶ added in v0.5.1
func WithRCMaxSize(n int) RecursiveChunkerOption
WithRCMaxSize sets the maximum chunk size in estimator units. Default: 500.
func WithRCOverlap ¶ added in v0.5.1
func WithRCOverlap(n int) RecursiveChunkerOption
WithRCOverlap sets the overlap budget in estimator units appended from the tail of chunk[i] to the head of chunk[i+1]. Default: 50.
func WithRCSeparators ¶ added in v0.5.1
func WithRCSeparators(seps []string) RecursiveChunkerOption
WithRCSeparators sets the ordered list of separators the chunker tries when splitting text. The first separator that produces multiple fragments is used; shorter separators are only tried when larger ones produce no split.
type SentenceChunker ¶ added in v0.5.1
type SentenceChunker struct {
MaxSize int // maximum chunk size in Estimator units
Overlap int // number of sentences shared between adjacent chunks
Estimator SizeEstimator // default: HeuristicTokenEstimator
}
SentenceChunker splits text at sentence boundaries and groups sentences into chunks that do not exceed MaxSize. Adjacent chunks share the last Overlap complete sentences for context, preserving semantic coherence better than token-level overlap.
ContentImage and ContentDocument blocks are silently ignored. Compatible with any text-only Embedder.
func NewSentenceChunker ¶ added in v0.5.1
func NewSentenceChunker(opts ...SentenceChunkerOption) *SentenceChunker
NewSentenceChunker creates a SentenceChunker with defaults: MaxSize=500, Overlap=1, Estimator=HeuristicTokenEstimator.
Example ¶
ExampleNewSentenceChunker shows grouping sentences into chunks that respect MaxSize, with one overlapping sentence between adjacent chunks for context.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
chunker := vector.NewSentenceChunker(
vector.WithSentenceMaxSize(20),
vector.WithSentenceOverlap(0),
vector.WithSentenceEstimator(&vector.CharEstimator{}),
)
content := vector.ChunkContent{
Blocks: []goagent.ContentBlock{
goagent.TextBlock("Go is fast. It uses goroutines."),
},
}
chunks, err := chunker.Chunk(ctx, content)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(chunks))
fmt.Println(vector.ExtractText(chunks[0].Blocks))
fmt.Println(vector.ExtractText(chunks[1].Blocks))
}
Output: 2 Go is fast. It uses goroutines.
func (*SentenceChunker) Chunk ¶ added in v0.5.1
func (c *SentenceChunker) Chunk(ctx context.Context, content ChunkContent) ([]ChunkResult, error)
Chunk splits content into sentence-aware chunks. Returns nil when there is no text. When all sentences fit in a single chunk, chunk_index and chunk_total are not added to the metadata.
type SentenceChunkerOption ¶ added in v0.5.1
type SentenceChunkerOption func(*SentenceChunker)
SentenceChunkerOption configures a SentenceChunker.
func WithSentenceEstimator ¶ added in v0.5.1
func WithSentenceEstimator(e SizeEstimator) SentenceChunkerOption
WithSentenceEstimator sets the SizeEstimator used to measure chunk size. Default: HeuristicTokenEstimator.
func WithSentenceMaxSize ¶ added in v0.5.1
func WithSentenceMaxSize(n int) SentenceChunkerOption
WithSentenceMaxSize sets the maximum chunk size in estimator units. Default: 500.
func WithSentenceOverlap ¶ added in v0.5.1
func WithSentenceOverlap(n int) SentenceChunkerOption
WithSentenceOverlap sets the number of complete sentences carried from one chunk into the next. Unlike TextChunker, overlap is counted in sentences, not tokens, which preserves semantic coherence at boundaries. Default: 1.
type SizeEstimator ¶
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.
Example ¶
ExampleNewTextChunker shows splitting text at word boundaries when it exceeds MaxSize, with zero overlap between adjacent chunks.
package main
import (
"context"
"fmt"
"log"
"github.com/Germanblandin1/goagent"
"github.com/Germanblandin1/goagent/memory/vector"
)
func main() {
ctx := context.Background()
chunker := vector.NewTextChunker(
vector.WithMaxSize(10),
vector.WithOverlap(0),
vector.WithEstimator(&vector.CharEstimator{}),
)
content := vector.ChunkContent{
Blocks: []goagent.ContentBlock{goagent.TextBlock("hello world foo bar")},
}
chunks, err := chunker.Chunk(ctx, content)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(chunks))
fmt.Println(vector.ExtractText(chunks[0].Blocks))
fmt.Println(vector.ExtractText(chunks[1].Blocks))
}
Output: 2 hello world foo bar
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.